Support negation, parentheses. Always zero-extend registers for bugless memory operand usage

This commit is contained in:
mid 2024-06-12 11:17:09 +03:00
parent fa40a78546
commit 012320569e
7 changed files with 137 additions and 49 deletions

View File

@ -5,6 +5,8 @@
#include<string.h> #include<string.h>
#include<assert.h> #include<assert.h>
#include"x86.h"
#define REGS 4 #define REGS 4
static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}}; static const char *regs[REGS][3] = {{"al", "ax", "eax"}, {"bl", "bx", "ebx"}, {"cl", "cx", "ecx"}, {"dl", "dx", "edx"}};
@ -51,7 +53,7 @@ static const char *specexpr(AST *e) {
return spec(type_size(e->expression.type)); return spec(type_size(e->expression.type));
} }
static const char *xv(VarTableEntry *v) { static const char *xv_sz(VarTableEntry *v, int sz) {
assert(v->kind == VARTABLEENTRY_VAR); assert(v->kind == VARTABLEENTRY_VAR);
#define XVBUFS 8 #define XVBUFS 8
@ -64,7 +66,7 @@ static const char *xv(VarTableEntry *v) {
#ifdef DEBUG #ifdef DEBUG
snprintf(ret, XVBUFSZ, "@%i", v->data.var.color); snprintf(ret, XVBUFSZ, "@%i", v->data.var.color);
#else #else
snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][log_size(type_size(v->type))]); snprintf(ret, XVBUFSZ, "%s", regs[v->data.var.color][log_size(sz)]);
#endif #endif
bufidx = (bufidx + 1) % XVBUFS; bufidx = (bufidx + 1) % XVBUFS;
@ -72,6 +74,10 @@ static const char *xv(VarTableEntry *v) {
return ret; return ret;
} }
static const char *xv(VarTableEntry *v) {
return xv_sz(v, type_size(v->type));
}
static const char *xj(BinaryOp op) { static const char *xj(BinaryOp op) {
switch(op) { switch(op) {
case BINOP_EQUAL: return "e"; case BINOP_EQUAL: return "e";
@ -80,7 +86,7 @@ static const char *xj(BinaryOp op) {
} }
} }
static const char *xop(AST *e) { static const char *xop_sz(AST *e, int sz) {
#define XOPBUFS 16 #define XOPBUFS 16
#define XOPBUFSZ 24 #define XOPBUFSZ 24
static char bufs[XOPBUFS][XOPBUFSZ]; static char bufs[XOPBUFS][XOPBUFSZ];
@ -92,25 +98,32 @@ static const char *xop(AST *e) {
VarTableEntry *v = e->exprVar.thing; VarTableEntry *v = e->exprVar.thing;
if(v->kind == VARTABLEENTRY_VAR) { if(v->kind == VARTABLEENTRY_VAR) {
return xv(v); return xv_sz(v, sz);
} else if(v->kind == VARTABLEENTRY_SYMBOL) { } else if(v->kind == VARTABLEENTRY_SYMBOL) {
snprintf(ret, XOPBUFSZ, "[%s]", v->data.symbol.name); snprintf(ret, XOPBUFSZ, "[%s]", v->data.symbol.name);
} else abort(); } else abort();
} else if(e->nodeKind == AST_EXPR_PRIMITIVE) { } else if(e->nodeKind == AST_EXPR_PRIMITIVE) {
snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val); snprintf(ret, XOPBUFSZ, "%i", e->exprPrim.val);
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[0]->exprVar.thing, 4),
xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "[%s + %s]", snprintf(ret, XOPBUFSZ, "%s [%s + %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing)); xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && e->exprUnOp.operand->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operand->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "[%s + %i * %s]", snprintf(ret, XOPBUFSZ, "%s [%s + %i * %s]",
spec(sz),
e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, e->exprUnOp.operand->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val, e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val,
xv(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing)); xv_sz(e->exprUnOp.operand->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing, 4));
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name); snprintf(ret, XOPBUFSZ, "%s", e->exprUnOp.operand->exprVar.thing->data.symbol.name);
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) { } else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_VAR) {
snprintf(ret, XOPBUFSZ, "[%s]", xv(e->exprUnOp.operand->exprVar.thing)); snprintf(ret, XOPBUFSZ, "%s [%s]", spec(sz), xv_sz(e->exprUnOp.operand->exprVar.thing, 4));
} else { } else {
return NULL; return NULL;
} }
@ -120,6 +133,10 @@ static const char *xop(AST *e) {
return ret; return ret;
} }
static const char *xop(AST *e) {
return xop_sz(e, type_size(e->expression.type));
}
void cg_chunk(AST *a) { void cg_chunk(AST *a) {
AST *s = a->chunk.statementFirst; AST *s = a->chunk.statementFirst;
@ -201,9 +218,24 @@ void cg_chunk(AST *a) {
s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name, s->stmtAssign.to->exprBinOp.operands[0]->exprUnOp.operand->exprVar.thing->data.symbol.name,
xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing)); xv(s->stmtAssign.to->exprBinOp.operands[1]->exprVar.thing));
} else if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && s->stmtAssign.to->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR && s->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing->kind == VARTABLEENTRY_VAR) {
printf("lea %s, [%s + %i]\n",
xv_sz(s->stmtAssign.what->exprVar.thing, 4),
xv_sz(s->stmtAssign.to->exprBinOp.operands[0]->exprVar.thing, 4),
s->stmtAssign.to->exprBinOp.operands[1]->exprPrim.val);
} else if(is_xop(s->stmtAssign.what) != NULL && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && ast_expression_equal(s->stmtAssign.what, s->stmtAssign.to->exprUnOp.operand)) {
printf("neg %s\n", xop(s->stmtAssign.what));
} else { } else {
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop(s->stmtAssign.to)); if(is_xop(s->stmtAssign.to) == XOP_MEM && type_size(s->stmtAssign.what->expression.type) != type_size(s->stmtAssign.to->expression.type)) {
printf("movzx %s, %s\n", xop_sz(s->stmtAssign.what, 4), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
} else {
printf("mov %s, %s\n", xop(s->stmtAssign.what), xop_sz(s->stmtAssign.to, type_size(s->stmtAssign.what->expression.type)));
}
} }
} }
@ -241,7 +273,7 @@ void cg_chunk(AST *a) {
size_t lbl = nextLocalLabel++; size_t lbl = nextLocalLabel++;
printf("cmp %s %s, %s\n", specexpr(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1])); printf("cmp %s, %s\n", xop(s->stmtIf.expression->exprBinOp.operands[0]), xop(s->stmtIf.expression->exprBinOp.operands[1]));
printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl); printf("j%s .L%lu\n", xj(binop_comp_opposite(s->stmtIf.expression->exprBinOp.operator)), lbl);
cg_chunk(s->stmtIf.then); cg_chunk(s->stmtIf.then);
@ -363,4 +395,4 @@ nextColor:;
cg_chunk(a); cg_chunk(a);
free(vars); free(vars);
} }

View File

@ -11,37 +11,7 @@
// This pass, along with CG is strictly dependent on x86 and will fail for // This pass, along with CG is strictly dependent on x86 and will fail for
// any other architecture. // any other architecture.
// Can expression be expressed as a single x86 operand? #include"x86.h"
#define XOP_NOT_XOP 0
#define XOP_NOT_MEM 1
#define XOP_MEM 2
static int is_xop(AST *e) {
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_VAR) {
return e->exprVar.thing->kind == VARTABLEENTRY_VAR ? XOP_NOT_MEM : XOP_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == VARTABLEENTRY_SYMBOL) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *c = e->exprUnOp.operand;
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == VARTABLEENTRY_VAR) {
return XOP_MEM;
} else if(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && c->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && c->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR) {
if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
return XOP_MEM;
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && c->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && c->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val;
if(scale == 1 || scale == 2 || scale == 4 || scale == 8) {
return XOP_MEM;
}
}
}
}
return XOP_NOT_XOP;
}
static void varify_change_usedefs(AST *e, AST *oldStmt, AST *newStmt) { static void varify_change_usedefs(AST *e, AST *oldStmt, AST *newStmt) {
if(e->nodeKind == AST_EXPR_VAR) { if(e->nodeKind == AST_EXPR_VAR) {
@ -185,6 +155,28 @@ static AST *xopify(AST *tlc, AST *chunk, AST *stmtPrev, AST *stmt, AST *e) {
return varify(tlc, chunk, stmtPrev, stmt, e); return varify(tlc, chunk, stmtPrev, stmt, e);
} }
static UseDef *get_last_usedef_before_stmt(AST *tlc, VarTableEntry *v, AST *stmt) {
assert(v->kind == VARTABLEENTRY_VAR);
UseDef *ud = v->data.var.usedefFirst;
if(!ud) {
return NULL;
}
UseDef *udPrev = NULL;
while(ud) {
if(ast_stmt_is_after(tlc, ud->stmt, stmt)) {
break;
}
udPrev = ud;
ud = ud->next;
}
return udPrev;
}
static int dumben_chunk(AST *tlc, AST *chu) { static int dumben_chunk(AST *tlc, AST *chu) {
AST *sPrev = NULL; AST *sPrev = NULL;
AST *s = chu->chunk.statementFirst; AST *s = chu->chunk.statementFirst;
@ -238,6 +230,59 @@ static int dumben_chunk(AST *tlc, AST *chu) {
effective = 1; effective = 1;
} }
if(s->stmtAssign.what->nodeKind == AST_EXPR_VAR && s->stmtAssign.what->exprVar.thing->kind == VARTABLEENTRY_VAR && s->stmtAssign.to->nodeKind == AST_EXPR_UNARY_OP && s->stmtAssign.to->exprUnOp.operator == UNOP_NEGATE && (s->stmtAssign.to->exprUnOp.operand->nodeKind != AST_EXPR_VAR || s->stmtAssign.what->exprVar.thing != s->stmtAssign.to->exprUnOp.operand->exprVar.thing)) {
// Turn this:
// a = -b
// into
// a = b
// a = -a
// While keeping UD-chain valid.
// Currently only for variables. While this could work for any XOP, it requires the ability to deep-copy `a`, which is not trivial when taking into account UD-chains.
// TODO: Scrap incremental UD-chain modifications. Simply rebuild them after each pass.
ASTExprVar *ev[2];
for(int i = 0; i < 2; i++) {
ev[i] = malloc(sizeof(ASTExprVar));
ev[i]->nodeKind = AST_EXPR_VAR;
ev[i]->type = s->stmtAssign.what->expression.type;
ev[i]->thing = s->stmtAssign.what->exprVar.thing;
}
AST *negation = s->stmtAssign.to;
s->stmtAssign.to = negation->exprUnOp.operand;
negation->exprUnOp.operand = ev[0];
AST *assign2 = malloc(sizeof(ASTStmtAssign));
assign2->nodeKind = AST_STMT_ASSIGN;
assign2->stmtAssign.what = ev[1];
assign2->stmtAssign.to = negation;
assign2->statement.next = s->statement.next;
s->statement.next = assign2;
UseDef *link = get_last_usedef_before_stmt(tlc, ev[0]->thing, assign2->statement.next);
UseDef *ud[2];
ud[0] = malloc(sizeof(UseDef));
ud[1] = malloc(sizeof(UseDef));
ud[0]->stmt = assign2;
ud[0]->use = (AST*) ev[1];
ud[0]->def = s;
ud[0]->next = ud[1];
ud[1]->stmt = assign2;
ud[1]->use = (AST*) ev[0];
ud[1]->def = s;
ud[1]->next = link->next;
link->next = ud[0];
effective = 1;
}
} else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) { } else if(s->nodeKind == AST_STMT_EXPR && s->stmtExpr.expr->nodeKind == AST_EXPR_CALL) {
int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount; int argCount = s->stmtExpr.expr->exprCall.what->expression.type->function.argCount;
@ -260,4 +305,4 @@ static int dumben_chunk(AST *tlc, AST *chu) {
void dumben_go(AST* tlc) { void dumben_go(AST* tlc) {
while(dumben_chunk(tlc, tlc)); while(dumben_chunk(tlc, tlc));
} }

View File

@ -285,6 +285,10 @@ AST *nct_parse_expression(Parser *P, int lOP) {
ret->length = tok.length; ret->length = tok.length;
return (AST*) ret; return (AST*) ret;
} else if(maybe(P, TOKEN_PAREN_L)) {
AST *e = nct_parse_expression(P, 0);
expect(P, TOKEN_PAREN_R);
return e;
} }
} else if(lOP == 4) { } else if(lOP == 4) {
if(maybe(P, TOKEN_STAR)) { if(maybe(P, TOKEN_STAR)) {

View File

@ -1,2 +1,6 @@
u32 x: 123; u32 x: 123;
u33 y: 5; u33 y: 5;
u3 *o = 5000;
u3 z = *o;
u3 p = *(o + z);

View File

@ -2,10 +2,11 @@ u16 x: 5;
loop { loop {
u16* y = 257; u16* y = 257;
u9 w = -4; u9 w = -4;
u4 z = 3 + *y; u16 p = *y;
u4 z = p + 3;
u2 o = -w; u2 o = -w;
if(x != 0) { if(x != 0) {
break; break;
} }
} }

View File

@ -4,4 +4,6 @@ loop {
a = 3; a = 3;
u8 b = 1; u8 b = 1;
u8 c = 2;
} }

View File

@ -1,5 +1,5 @@
u8 a = 5; u8 a = 5;
if(a) { if(a != 0) {
u8 a = 10; /* Should not cause scoping errors. */ u8 a = 10; /* Should not cause scoping errors. */
} }
u8 b = 15; /* `a` in the if statement scope should be free'd. */ u8 b = 15; /* `a` in the if statement scope should be free'd. */