astgen: fix for loop expressions

also rename the ZIR instruction `deref_node` to `load`.
This commit is contained in:
Andrew Kelley
2021-03-25 19:25:26 -07:00
parent 399bb2e154
commit b9c5a1fdf5
5 changed files with 118 additions and 130 deletions

View File

@@ -35,3 +35,6 @@ Performance optimizations to look into:
* enum literals can use small strings
* string literals can use small strings
* don't need the Sema coercion on condbr condition, it's done with result locations
* astgen for loops using pointer arithmetic because it's faster and if the programmer
wants an index capture, that will just be a convenience variable that zig sets up
independently.

View File

@@ -160,7 +160,7 @@ pub fn analyzeBody(
.@"const" => try sema.zirConst(block, inst),
.decl_ref => try sema.zirDeclRef(block, inst),
.decl_val => try sema.zirDeclVal(block, inst),
.deref_node => try sema.zirDerefNode(block, inst),
.load => try sema.zirLoad(block, inst),
.div => try sema.zirArithmetic(block, inst),
.elem_ptr => try sema.zirElemPtr(block, inst),
.elem_ptr_node => try sema.zirElemPtrNode(block, inst),
@@ -576,7 +576,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) In
return sema.mod.failWithOwnedErrorMsg(&block.base, msg);
}
const result_ptr = try sema.namedFieldPtr(block, src, array_ptr, "len", src);
return sema.analyzeDeref(block, src, result_ptr, result_ptr.src);
return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
}
fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -1911,7 +1911,7 @@ fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
const object = try sema.resolveInst(extra.lhs);
const object_ptr = try sema.analyzeRef(block, src, object);
const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
return sema.analyzeDeref(block, src, result_ptr, result_ptr.src);
return sema.analyzeLoad(block, src, result_ptr, result_ptr.src);
}
fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -1939,7 +1939,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name);
const object_ptr = try sema.analyzeRef(block, src, object);
const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src);
return sema.analyzeDeref(block, src, result_ptr, src);
return sema.analyzeLoad(block, src, result_ptr, src);
}
fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2060,7 +2060,7 @@ fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
const array_ptr = try sema.analyzeRef(block, sema.src, array);
const elem_index = try sema.resolveInst(bin_inst.rhs);
const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src);
return sema.analyzeDeref(block, sema.src, result_ptr, sema.src);
return sema.analyzeLoad(block, sema.src, result_ptr, sema.src);
}
fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2075,7 +2075,7 @@ fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE
const array_ptr = try sema.analyzeRef(block, src, array);
const elem_index = try sema.resolveInst(extra.rhs);
const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
return sema.analyzeDeref(block, src, result_ptr, src);
return sema.analyzeLoad(block, src, result_ptr, src);
}
fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2183,7 +2183,7 @@ fn zirSwitchBr(
const target_ptr = try sema.resolveInst(inst.positionals.target);
const target = if (ref)
try sema.analyzeDeref(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)
try sema.analyzeLoad(parent_block, inst.base.src, target_ptr, inst.positionals.target.src)
else
target_ptr;
try sema.validateSwitch(parent_block, target, inst);
@@ -2639,7 +2639,7 @@ fn analyzeArithmetic(
return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
}
fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
fn zirLoad(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -2647,7 +2647,7 @@ fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErr
const src = inst_data.src();
const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node };
const ptr = try sema.resolveInst(inst_data.operand);
return sema.analyzeDeref(block, src, ptr, ptr_src);
return sema.analyzeLoad(block, src, ptr, ptr_src);
}
fn zirAsm(
@@ -2958,7 +2958,7 @@ fn zirIsNullPtr(
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ptr = try sema.resolveInst(inst_data.operand);
const loaded = try sema.analyzeDeref(block, src, ptr, src);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsNull(block, src, loaded, invert_logic);
}
@@ -2978,7 +2978,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerErro
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const ptr = try sema.resolveInst(inst_data.operand);
const loaded = try sema.analyzeDeref(block, src, ptr, src);
const loaded = try sema.analyzeLoad(block, src, ptr, src);
return sema.analyzeIsErr(block, src, loaded);
}
@@ -3343,7 +3343,7 @@ fn namedFieldPtr(
},
.Type => {
_ = try sema.resolveConstValue(block, object_ptr.src, object_ptr);
const result = try sema.analyzeDeref(block, src, object_ptr, object_ptr.src);
const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr.src);
const val = result.value().?;
const child_type = try val.toType(sema.arena);
switch (child_type.zigTypeTag()) {
@@ -3409,7 +3409,7 @@ fn elemPtr(
if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) {
// we have to deref the ptr operand to get the actual array pointer
const array_ptr_deref = try sema.analyzeDeref(block, src, array_ptr, array_ptr.src);
const array_ptr_deref = try sema.analyzeLoad(block, src, array_ptr, array_ptr.src);
if (array_ptr_deref.value()) |array_ptr_val| {
if (elem_index.value()) |index_val| {
// Both array pointer and index are compile-time known.
@@ -3669,7 +3669,7 @@ fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst:
fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
const decl_ref = try sema.analyzeDeclRef(block, src, decl);
return sema.analyzeDeref(block, src, decl_ref, src);
return sema.analyzeLoad(block, src, decl_ref, src);
}
fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst {
@@ -3737,7 +3737,7 @@ fn analyzeRef(
return block.addUnOp(src, ptr_type, .ref, operand);
}
fn analyzeDeref(
fn analyzeLoad(
sema: *Sema,
block: *Scope.Block,
src: LazySrcLoc,

View File

@@ -443,7 +443,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
.deref => {
const lhs = try expr(mod, scope, .none, node_datas[node].lhs);
const result = try gz.addUnNode(.deref_node, lhs, node);
const result = try gz.addUnNode(.load, lhs, node);
return rvalue(mod, scope, rl, result, node);
},
.address_of => {
@@ -1042,7 +1042,7 @@ fn blockExprStmts(
.coerce_result_ptr,
.decl_ref,
.decl_val,
.deref_node,
.load,
.div,
.elem_ptr,
.elem_val,
@@ -1396,7 +1396,7 @@ fn assignOp(
const gz = scope.getGenZir();
const lhs_ptr = try lvalExpr(mod, scope, node_datas[infix_node].lhs);
const lhs = try gz.addUnNode(.deref_node, lhs_ptr, infix_node);
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
const rhs = try expr(mod, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
@@ -2105,7 +2105,7 @@ fn whileExpr(
try checkLabelRedefinition(mod, scope, label_token);
}
const parent_gz = scope.getGenZir();
const is_inline = while_full.inline_token != null;
const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
const loop_block = try parent_gz.addBlock(loop_tag, node);
try parent_gz.instructions.append(mod.gpa, loop_block);
@@ -2149,7 +2149,6 @@ fn whileExpr(
// TODO avoid emitting the continue expr when there
// are no jumps to it. This happens when the last statement of a while body is noreturn
// and there are no `continue` statements.
// The "repeat" at the end of a loop body is implied.
if (while_full.ast.cont_expr != 0) {
_ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
}
@@ -2236,44 +2235,32 @@ fn forExpr(
node: ast.Node.Index,
for_full: ast.full.While,
) InnerError!zir.Inst.Ref {
if (true) @panic("TODO update for zir-memory-layout");
if (for_full.label_token) |label_token| {
try checkLabelRedefinition(mod, scope, label_token);
}
if (for_full.inline_token) |inline_token| {
return mod.failTok(scope, inline_token, "TODO inline for", .{});
}
// Set up variables and constants.
const parent_gz = scope.getGenZir();
const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
const tree = parent_gz.tree();
const main_tokens = tree.nodes.items(.main_token);
const token_tags = tree.tokens.items(.tag);
const for_src = token_starts[for_full.ast.while_token];
const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
const index_ptr = blk: {
const usize_type = try addZIRInstConst(mod, scope, for_src, .{
.ty = Type.initTag(.type),
.val = Value.initTag(.usize_type),
});
const index_ptr = try addZIRUnOp(mod, scope, for_src, .alloc, usize_type);
const index_ptr = try parent_gz.addUnNode(.alloc, .usize_type, node);
// initialize to zero
const zero = try addZIRInstConst(mod, scope, for_src, .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.zero),
});
_ = try addZIRBinOp(mod, scope, for_src, .store, index_ptr, zero);
_ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
break :blk index_ptr;
};
const array_ptr = try expr(mod, scope, .ref, for_full.ast.cond_expr);
const cond_src = token_starts[tree.firstToken(for_full.ast.cond_expr)];
const len = try addZIRUnOp(mod, scope, cond_src, .indexable_ptr_len, array_ptr);
const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
const loop_block = try parent_gz.addBlock(loop_tag, node);
try parent_gz.instructions.append(mod.gpa, loop_block);
var loop_scope: Scope.GenZir = .{
.parent = scope,
.decl = scope.ownerDecl().?,
.arena = scope.arena(),
.zir_code = parent_gz.zir_code,
.force_comptime = parent_gz.force_comptime,
.instructions = .{},
};
@@ -2282,66 +2269,49 @@ fn forExpr(
var cond_scope: Scope.GenZir = .{
.parent = &loop_scope.base,
.decl = loop_scope.decl,
.arena = loop_scope.arena,
.zir_code = parent_gz.zir_code,
.force_comptime = loop_scope.force_comptime,
.instructions = .{},
};
defer cond_scope.instructions.deinit(mod.gpa);
// check condition i < array_expr.len
const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
.condition = cond,
.then_body = undefined, // populated below
.else_body = undefined, // populated below
}, .{});
const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
.instructions = try loop_scope.arena.dupe(zir.Inst.Ref, cond_scope.instructions.items),
const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, zir.Inst.Bin{
.lhs = index,
.rhs = len,
});
// increment index variable
const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.one),
});
const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
_ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
const condbr = try cond_scope.addCondBr(condbr_tag, node);
const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
const cond_block = try loop_scope.addBlock(block_tag, node);
try loop_scope.instructions.append(mod.gpa, cond_block);
try cond_scope.setBlockBody(cond_block);
const loop = try scope.arena().create(zir.Inst.Loop);
loop.* = .{
.base = .{
.tag = .loop,
.src = for_src,
},
.positionals = .{
.body = .{
.instructions = try scope.arena().dupe(zir.Inst.Ref, loop_scope.instructions.items),
},
},
.kw_args = .{},
};
const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
.instructions = try scope.arena().dupe(zir.Inst.Ref, &[1]zir.Inst.Ref{&loop.base}),
// Increment the index variable.
const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
const index_plus_one = try loop_scope.addPlNode(.add, node, zir.Inst.Bin{
.lhs = index_2,
.rhs = .one_usize,
});
loop_scope.break_block = for_block;
_ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
_ = try loop_scope.addNode(repeat_tag, node);
try loop_scope.setBlockBody(loop_block);
loop_scope.break_block = loop_block;
loop_scope.continue_block = cond_block;
if (for_full.label_token) |label_token| {
loop_scope.label = @as(?Scope.GenZir.Label, Scope.GenZir.Label{
.token = label_token,
.block_inst = for_block,
.block_inst = loop_block,
});
}
// while body
const then_src = token_starts[tree.lastToken(for_full.ast.then_expr)];
var then_scope: Scope.GenZir = .{
.parent = &cond_scope.base,
.decl = cond_scope.decl,
.arena = cond_scope.arena,
.zir_code = parent_gz.zir_code,
.force_comptime = cond_scope.force_comptime,
.instructions = .{},
};
@@ -2375,6 +2345,7 @@ fn forExpr(
.gen_zir = &then_scope,
.name = index_name,
.ptr = index_ptr,
.src = parent_gz.tokSrcLoc(index_token),
};
break :blk &index_scope.base;
};
@@ -2382,34 +2353,36 @@ fn forExpr(
loop_scope.break_count += 1;
const then_result = try expr(mod, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
// else branch
var else_scope: Scope.GenZir = .{
.parent = &cond_scope.base,
.decl = cond_scope.decl,
.arena = cond_scope.arena,
.zir_code = parent_gz.zir_code,
.force_comptime = cond_scope.force_comptime,
.instructions = .{},
};
defer else_scope.instructions.deinit(mod.gpa);
const else_node = for_full.ast.else_expr;
const else_info: struct { src: usize, result: ?*zir.Inst } = if (else_node != 0) blk: {
const else_info: struct {
src: ast.Node.Index,
result: zir.Inst.Ref,
} = if (else_node != 0) blk: {
loop_scope.break_count += 1;
const sub_scope = &else_scope.base;
break :blk .{
.src = token_starts[tree.lastToken(else_node)],
.src = else_node,
.result = try expr(mod, sub_scope, loop_scope.break_result_loc, else_node),
};
} else .{
.src = token_starts[tree.lastToken(for_full.ast.then_expr)],
.result = null,
.src = for_full.ast.then_expr,
.result = .none,
};
if (loop_scope.label) |some| {
if (!some.used) {
return mod.fail(scope, token_starts[some.token], "unused for loop label", .{});
return mod.failTok(scope, some.token, "unused for loop label", .{});
}
}
const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
return finishThenElseBlock(
mod,
scope,
@@ -2420,13 +2393,13 @@ fn forExpr(
&else_scope,
condbr,
cond,
then_src,
for_full.ast.then_expr,
else_info.src,
then_result,
else_info.result,
for_block,
loop_block,
cond_block,
.@"break",
break_tag,
);
}
@@ -2862,7 +2835,7 @@ fn identifier(
const local_ptr = s.cast(Scope.LocalPtr).?;
if (mem.eql(u8, local_ptr.name, ident_name)) {
if (rl == .ref) return local_ptr.ptr;
const loaded = try gz.addUnNode(.deref_node, local_ptr.ptr, ident);
const loaded = try gz.addUnNode(.load, local_ptr.ptr, ident);
return rvalue(mod, scope, rl, loaded, ident);
}
s = local_ptr.parent;

View File

@@ -281,7 +281,7 @@ pub const Inst = struct {
decl_val,
/// Load the value from a pointer. Assumes `x.*` syntax.
/// Uses `un_node` field. AST node is the `x.*` syntax.
deref_node,
load,
/// Arithmetic division. Asserts no integer overflow.
/// Uses the `pl_node` union field. Payload is `Bin`.
div,
@@ -661,7 +661,7 @@ pub const Inst = struct {
.dbg_stmt_node,
.decl_ref,
.decl_val,
.deref_node,
.load,
.div,
.elem_ptr,
.elem_val,
@@ -841,6 +841,10 @@ pub const Inst = struct {
bool_true,
/// `false`
bool_false,
/// `0` (usize)
zero_usize,
/// `1` (usize)
one_usize,
_,
@@ -1016,10 +1020,18 @@ pub const Inst = struct {
.ty = Type.initTag(.comptime_int),
.val = Value.initTag(.zero),
},
.zero_usize = .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.zero),
},
.one = .{
.ty = Type.initTag(.comptime_int),
.val = Value.initTag(.one),
},
.one_usize = .{
.ty = Type.initTag(.usize),
.val = Value.initTag(.one),
},
.void_value = .{
.ty = Type.initTag(.void),
.val = Value.initTag(.void_value),
@@ -1377,7 +1389,7 @@ const Writer = struct {
.call_none,
.call_none_chkused,
.compile_error,
.deref_node,
.load,
.ensure_result_used,
.ensure_result_non_error,
.import,

View File

@@ -968,37 +968,37 @@ pub fn addCases(ctx: *TestContext) !void {
);
// Basic for loop
//case.addCompareOutput(
// \\export fn _start() noreturn {
// \\ for ("hello") |_| print();
// \\
// \\ exit();
// \\}
// \\
// \\fn print() void {
// \\ asm volatile ("syscall"
// \\ :
// \\ : [number] "{rax}" (1),
// \\ [arg1] "{rdi}" (1),
// \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
// \\ [arg3] "{rdx}" (6)
// \\ : "rcx", "r11", "memory"
// \\ );
// \\ return;
// \\}
// \\
// \\fn exit() noreturn {
// \\ asm volatile ("syscall"
// \\ :
// \\ : [number] "{rax}" (231),
// \\ [arg1] "{rdi}" (0)
// \\ : "rcx", "r11", "memory"
// \\ );
// \\ unreachable;
// \\}
//,
// "hello\nhello\nhello\nhello\nhello\n",
//);
case.addCompareOutput(
\\export fn _start() noreturn {
\\ for ("hello") |_| print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
\\ [arg3] "{rdx}" (6)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"hello\nhello\nhello\nhello\nhello\n",
);
}
//{