lower+backend: 배열, 경계검사, 구조체, 그리고 실행 테스트 스위트
인덱스는 부호 없는 비교와 트랩으로 펴진다. --no-checks 는 메시지가 아니라 비교와 분기 자체를 없앤다 -- 그게 그 플래그의 전부다. 버그 셋: - 값으로 넘긴 구조체 파라미터는 주소로 도착하는데 lowering 이 그걸 몰라서 포인터를 구조체로 읽었다. 변수마다 by_address 를 기록한다. - 덩어리 반환의 숨은 결과 인자를 지역이 아니라 임시값으로 다뤘다. - store 에 폭이 없어서 1바이트 bool 을 4바이트로 썼다. 옆 지역변수가 뭉개졌고 logic 프로그램이 틀린 답을 냈다. tests/exec.py 가 새 스위트다. run.py 는 컴파일러가 프로그램에 대해 뭐라고 하는지 보고, 이쪽은 프로그램이 실제로 무엇을 하는지 본다. 보고만 되고 방출되지 않는 경계검사는 저기서는 통과하고 여기서는 실패한다. run.py 194/194, exec.py 6/6.
This commit is contained in:
+3
-2
@@ -144,9 +144,10 @@ unsigned fe_ir_load(FeIrModule *m, FeIrBlock *b, FeIrType t, FeIrPlace p)
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned value)
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned value,
|
||||
FeIrType t)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_STORE, FE_IR_VOID);
|
||||
FeIrValue *v = emit(m, b, FE_IR_STORE, t);
|
||||
if (!v) return;
|
||||
v->place = p;
|
||||
v->a = value;
|
||||
|
||||
+4
-1
@@ -160,7 +160,10 @@ FeIrPlace fe_ir_at_temp(unsigned temp, long offset);
|
||||
/* Instructions. Each returns the destination temporary where there is one. */
|
||||
unsigned fe_ir_const(FeIrModule *m, FeIrBlock *b, FeIrType t, long v);
|
||||
unsigned fe_ir_load(FeIrModule *m, FeIrBlock *b, FeIrType t, FeIrPlace p);
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned v);
|
||||
/* `t` is how wide the write is. Without it a one-byte value would be stored
|
||||
four bytes wide and take its neighbours with it. */
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned v,
|
||||
FeIrType t);
|
||||
unsigned fe_ir_addr(FeIrModule *m, FeIrBlock *b, FeIrPlace p);
|
||||
unsigned fe_ir_binary(FeIrModule *m, FeIrBlock *b, FeIrOp op, FeIrType t,
|
||||
unsigned a, unsigned c, int is_unsigned);
|
||||
|
||||
+136
-14
@@ -17,6 +17,9 @@
|
||||
typedef struct LowerVar {
|
||||
const char *cname;
|
||||
unsigned local;
|
||||
/* An aggregate parameter arrives as an address, so the slot holds a
|
||||
pointer and the value is one dereference away. */
|
||||
int by_address;
|
||||
} LowerVar;
|
||||
|
||||
typedef struct Lower {
|
||||
@@ -45,6 +48,8 @@ typedef struct Slot {
|
||||
|
||||
static Slot lower_expr(Lower *L, FeNode *n);
|
||||
static void lower_stmt(Lower *L, FeNode *n);
|
||||
static void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n,
|
||||
unsigned long size);
|
||||
|
||||
static void fail(Lower *L, const char *why, FeNode *n)
|
||||
{
|
||||
@@ -163,20 +168,19 @@ static unsigned declare_var(Lower *L, const char *cname, const FeType *t,
|
||||
if (L->var_count < LOWER_MAX_LOCALS) {
|
||||
L->vars[L->var_count].cname = cname;
|
||||
L->vars[L->var_count].local = local;
|
||||
L->vars[L->var_count].by_address = 0;
|
||||
++L->var_count;
|
||||
}
|
||||
return local;
|
||||
}
|
||||
|
||||
static int find_var(Lower *L, const char *cname, unsigned *out)
|
||||
static LowerVar *find_var(Lower *L, const char *cname)
|
||||
{
|
||||
unsigned i;
|
||||
if (!cname) return 0;
|
||||
for (i = L->var_count; i > 0; --i)
|
||||
if (L->vars[i - 1].cname && strcmp(L->vars[i - 1].cname, cname) == 0) {
|
||||
*out = L->vars[i - 1].local;
|
||||
return 1;
|
||||
}
|
||||
if (L->vars[i - 1].cname && strcmp(L->vars[i - 1].cname, cname) == 0)
|
||||
return &L->vars[i - 1];
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -187,6 +191,50 @@ static FeIrBlock *new_block(Lower *L)
|
||||
return fe_ir_block(L->m, L->fn);
|
||||
}
|
||||
|
||||
/* A check that must hold. `ok` is a condition; when it is false the program
|
||||
stops where it is. `--no-checks` removes the comparison and the branch, not
|
||||
just the message, which is the whole point of the flag. */
|
||||
static void guard(Lower *L, unsigned ok, FeIrTrap reason, unsigned long line)
|
||||
{
|
||||
FeIrBlock *bad = new_block(L);
|
||||
FeIrBlock *cont = new_block(L);
|
||||
fe_ir_br(L->b, ok, cont->id, bad->id);
|
||||
L->b = bad;
|
||||
fe_ir_trap(L->b, reason, line);
|
||||
L->b = cont;
|
||||
}
|
||||
|
||||
/* Somewhere to build an aggregate that has no home of its own yet. */
|
||||
static unsigned scratch(Lower *L, const FeType *t, const char *why)
|
||||
{
|
||||
return fe_ir_local(L->m, L->fn, ir_type(t), ir_size(t), ir_align(t), why);
|
||||
}
|
||||
|
||||
/* A slice is a pointer and a length, in that order. Both the compiler and the
|
||||
runtime read it this way, so the offsets live here and nowhere else. */
|
||||
#define SLICE_PTR_OFFSET 0L
|
||||
#define SLICE_LEN_OFFSET 4L
|
||||
|
||||
/* The number of elements an indexable place holds, and where the first element
|
||||
is. An array is its own storage; a slice points at someone else's. */
|
||||
static void indexable_parts(Lower *L, Slot base, const FeType *t,
|
||||
unsigned *data, unsigned *length, FeNode *n)
|
||||
{
|
||||
if (t && t->kind == FE_TYPE_ARRAY) {
|
||||
*data = as_address(L, base, n);
|
||||
*length = fe_ir_const(L->m, L->b, FE_IR_I32, (long)t->length);
|
||||
return;
|
||||
}
|
||||
if (!base.is_place) { fail(L, "a slice with no place", n); *data = 0; *length = 0; return; }
|
||||
*data = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_temp(as_address(L, base, n), SLICE_PTR_OFFSET));
|
||||
{
|
||||
FeIrPlace lp = base.place;
|
||||
lp.offset += SLICE_LEN_OFFSET;
|
||||
*length = fe_ir_load(L->m, L->b, FE_IR_I32, lp);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------- expressions --- */
|
||||
|
||||
static FeIrOp binary_op(const char *op, int *is_cmp)
|
||||
@@ -268,12 +316,12 @@ static Slot lower_logical(Lower *L, FeNode *n, int is_and)
|
||||
unsigned right;
|
||||
L->b = entry;
|
||||
left = as_value(L, lower_expr(L, n->a), n->a);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(result, 0), left);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(result, 0), left, FE_IR_I8);
|
||||
if (is_and) fe_ir_br(L->b, left, rhs->id, join->id);
|
||||
else fe_ir_br(L->b, left, join->id, rhs->id);
|
||||
L->b = rhs;
|
||||
right = as_value(L, lower_expr(L, n->b), n->b);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(result, 0), right);
|
||||
fe_ir_store(L->m, L->b, fe_ir_at_local(result, 0), right, FE_IR_I8);
|
||||
fe_ir_jmp(L->b, join->id);
|
||||
L->b = join;
|
||||
return slot_place(fe_ir_at_local(result, 0), FE_IR_I8, 1);
|
||||
@@ -329,9 +377,15 @@ static Slot lower_expr(Lower *L, FeNode *n)
|
||||
literal_value(n)),
|
||||
it == FE_IR_VOID ? FE_IR_I32 : it);
|
||||
case FE_N_IDENT: {
|
||||
unsigned local;
|
||||
if (find_var(L, n->cname, &local))
|
||||
return slot_place(fe_ir_at_local(local, 0), it, ir_size(t));
|
||||
LowerVar *var = find_var(L, n->cname);
|
||||
if (var) {
|
||||
if (var->by_address) {
|
||||
unsigned p = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(var->local, 0));
|
||||
return slot_place(fe_ir_at_temp(p, 0), it, ir_size(t));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(var->local, 0), it, ir_size(t));
|
||||
}
|
||||
if (n->cname)
|
||||
return slot_place(fe_ir_at_global(n->cname, 0), it, ir_size(t));
|
||||
fail(L, "an unresolved name", n);
|
||||
@@ -380,6 +434,19 @@ static Slot lower_expr(Lower *L, FeNode *n)
|
||||
unsigned p = as_value(L, lower_expr(L, n->a), n->a);
|
||||
return slot_place(fe_ir_at_temp(p, 0), it, ir_size(t));
|
||||
}
|
||||
/* `.n` is how many elements there are, which an array knows at
|
||||
compile time and a slice carries beside its pointer. */
|
||||
if (n->b && n->b->text && !strcmp(n->b->text, "n")) {
|
||||
FeType *bt = n->a ? n->a->sem_type : 0;
|
||||
Slot base;
|
||||
if (bt && bt->kind == FE_TYPE_ARRAY)
|
||||
return slot_value(fe_ir_const(L->m, L->b, FE_IR_I32,
|
||||
(long)bt->length), FE_IR_I32);
|
||||
base = lower_expr(L, n->a);
|
||||
if (!base.is_place) { fail(L, "a length of a temporary", n); return slot_void(); }
|
||||
base.place.offset += SLICE_LEN_OFFSET;
|
||||
return slot_place(base.place, FE_IR_I32, 4);
|
||||
}
|
||||
/* A field is a constant offset from the base. */
|
||||
{
|
||||
FeType *base = n->a ? n->a->sem_type : 0;
|
||||
@@ -400,6 +467,58 @@ static Slot lower_expr(Lower *L, FeNode *n)
|
||||
b.place.offset += (long)field->offset;
|
||||
return slot_place(b.place, it, ir_size(t));
|
||||
}
|
||||
case FE_N_INDEX: {
|
||||
FeType *bt = n->a ? n->a->sem_type : 0;
|
||||
FeType *elem = bt ? bt->elem : 0;
|
||||
Slot base;
|
||||
unsigned data;
|
||||
unsigned length;
|
||||
unsigned index;
|
||||
unsigned scale;
|
||||
unsigned offset;
|
||||
unsigned addr;
|
||||
if (n->c || !n->b) { fail(L, "a slice expression", n); return slot_void(); }
|
||||
base = lower_expr(L, n->a);
|
||||
indexable_parts(L, base, bt, &data, &length, n);
|
||||
index = as_value(L, lower_expr(L, n->b), n->b);
|
||||
if (!L->c->no_checks) {
|
||||
unsigned ok = fe_ir_binary(L->m, L->b, FE_IR_LT, FE_IR_I32,
|
||||
index, length, 1);
|
||||
guard(L, ok, FE_TRAP_BOUNDS, n->loc.line);
|
||||
}
|
||||
scale = fe_ir_const(L->m, L->b, FE_IR_I32, (long)ir_size(elem));
|
||||
offset = fe_ir_binary(L->m, L->b, FE_IR_MUL, FE_IR_I32, index, scale, 1);
|
||||
addr = fe_ir_binary(L->m, L->b, FE_IR_ADD, FE_IR_PTR, data, offset, 1);
|
||||
return slot_place(fe_ir_at_temp(addr, 0), ir_type(elem), ir_size(elem));
|
||||
}
|
||||
case FE_N_ARRAY_INIT: {
|
||||
unsigned local = scratch(L, t, "array");
|
||||
FeType *elem = t ? t->elem : 0;
|
||||
unsigned long step = ir_size(elem);
|
||||
long at = 0;
|
||||
FeNode *x;
|
||||
for (x = n->children; x; x = x->next) {
|
||||
Slot v = lower_expr(L, x);
|
||||
store_into(L, fe_ir_at_local(local, at), v, x, step);
|
||||
at += (long)step;
|
||||
}
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
case FE_N_STRUCT_INIT: {
|
||||
unsigned local = scratch(L, t, "struct");
|
||||
FeNode *f;
|
||||
for (f = n->children; f; f = f->next) {
|
||||
FeFieldType *field;
|
||||
Slot v;
|
||||
if (f->kind != FE_N_FIELD) continue;
|
||||
field = fe_type_field(t, f->text);
|
||||
if (!field) { fail(L, "an unresolved field", f); return slot_void(); }
|
||||
v = lower_expr(L, f->a);
|
||||
store_into(L, fe_ir_at_local(local, (long)field->offset), v, f,
|
||||
ir_size(field->type));
|
||||
}
|
||||
return slot_place(fe_ir_at_local(local, 0), FE_IR_MEM, ir_size(t));
|
||||
}
|
||||
case FE_N_CALL:
|
||||
return lower_call(L, n);
|
||||
case FE_N_EXPR:
|
||||
@@ -420,7 +539,7 @@ static void store_into(Lower *L, FeIrPlace dst, Slot value, FeNode *n,
|
||||
fe_ir_copy(L->m, L->b, dst, value.place, size);
|
||||
return;
|
||||
}
|
||||
fe_ir_store(L->m, L->b, dst, as_value(L, value, n));
|
||||
fe_ir_store(L->m, L->b, dst, as_value(L, value, n), value.type);
|
||||
}
|
||||
|
||||
static void lower_return(Lower *L, FeNode *n)
|
||||
@@ -429,8 +548,9 @@ static void lower_return(Lower *L, FeNode *n)
|
||||
if (!n->a) { fe_ir_ret(L->b, 0, 0); return; }
|
||||
v = lower_expr(L, n->a);
|
||||
if (L->fn->returns_by_address) {
|
||||
store_into(L, fe_ir_at_temp(L->ret_local, 0), v, n,
|
||||
ir_size(L->ret_type));
|
||||
unsigned dst = fe_ir_load(L->m, L->b, FE_IR_PTR,
|
||||
fe_ir_at_local(L->ret_local, 0));
|
||||
store_into(L, fe_ir_at_temp(dst, 0), v, n, ir_size(L->ret_type));
|
||||
fe_ir_ret(L->b, 0, 0);
|
||||
return;
|
||||
}
|
||||
@@ -550,13 +670,15 @@ static void lower_fn(Lower *L, FeNode *fn)
|
||||
for (p = fn->a ? fn->a->children : 0; p; p = p->next) {
|
||||
FeType *pt = fe_type_from_ast(&L->c->types, p->a);
|
||||
/* An aggregate parameter arrives as an address. */
|
||||
unsigned local = ir_type(pt) == FE_IR_MEM
|
||||
int by_address = ir_type(pt) == FE_IR_MEM;
|
||||
unsigned local = by_address
|
||||
? fe_ir_local(L->m, f, FE_IR_PTR, 4, 4, p->text)
|
||||
: fe_ir_local(L->m, f, ir_type(pt), ir_size(pt), ir_align(pt),
|
||||
p->text);
|
||||
if (L->var_count < LOWER_MAX_LOCALS) {
|
||||
L->vars[L->var_count].cname = p->cname;
|
||||
L->vars[L->var_count].local = local;
|
||||
L->vars[L->var_count].by_address = by_address;
|
||||
++L->var_count;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -204,9 +204,8 @@ static void emit_value(const Frame *fr, const FeIrValue *v, FILE *out)
|
||||
load_place_base(fr, &v->place, out);
|
||||
place_addr(fr, &v->place, addr);
|
||||
load_temp(fr, v->a, "eax", out);
|
||||
fprintf(out, " mov %s %s, %s\n", word_of(v->type == FE_IR_VOID
|
||||
? FE_IR_I32 : v->type), addr, reg_of(v->type == FE_IR_VOID
|
||||
? FE_IR_I32 : v->type, 0));
|
||||
fprintf(out, " mov %s %s, %s\n", word_of(v->type), addr,
|
||||
reg_of(v->type, 0));
|
||||
break;
|
||||
case FE_IR_ADDR:
|
||||
load_place_base(fr, &v->place, out);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// EXIT:10
|
||||
unit arith;
|
||||
|
||||
fn main() -> i32 {
|
||||
let a: i32 = 7;
|
||||
let b: i32 = 6;
|
||||
var r: i32 = a * b;
|
||||
r = r - 2;
|
||||
r = r / 4;
|
||||
if r == 10 { return r; }
|
||||
return 99;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// EXIT:100
|
||||
unit array;
|
||||
|
||||
fn main() -> i32 {
|
||||
let a: [4]i32 = [10, 20, 30, 40];
|
||||
var sum: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < 4 {
|
||||
sum = sum + a[i];
|
||||
i = i + 1;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// EXIT:3
|
||||
// OUTPUT:index out of bounds
|
||||
// NOCHECKS:0
|
||||
unit bounds;
|
||||
|
||||
fn main() -> i32 {
|
||||
let a: [2]i32 = [1, 2];
|
||||
let x: i32 = a[2];
|
||||
return x - x;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// EXIT:1
|
||||
unit logic;
|
||||
|
||||
fn side(v: i32) -> bool { return v > 0; }
|
||||
|
||||
fn main() -> i32 {
|
||||
let a: bool = true and side(1);
|
||||
let b: bool = false or side(2);
|
||||
let c: bool = not side(0);
|
||||
if a and b and c { return 1; }
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// EXIT:55
|
||||
unit loopcall;
|
||||
|
||||
fn add(a: i32, b: i32) -> i32 { return a + b; }
|
||||
|
||||
fn main() -> i32 {
|
||||
var total: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < 10 {
|
||||
total = total + add(i, 1);
|
||||
i = i + 1;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// EXIT:97
|
||||
unit structs;
|
||||
|
||||
struct Point { x: i32, y: i32, }
|
||||
|
||||
fn make(a: i32, b: i32) -> Point { return Point{ x: a, y: b }; }
|
||||
fn swap(p: Point) -> Point { return Point{ x: p.y, y: p.x }; }
|
||||
|
||||
fn main() -> i32 {
|
||||
let p: Point = make(3, 8);
|
||||
let q: Point = swap(p);
|
||||
let grid: [3]Point = [make(1,1), make(2,2), make(3,3)];
|
||||
var s: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < 3 {
|
||||
s = s + grid[i].x * grid[i].y;
|
||||
i = i + 1;
|
||||
}
|
||||
return q.x * 10 + q.y + s;
|
||||
}
|
||||
Reference in New Issue
Block a user