authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-28 17:27:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 02:29:36-07:00
log5ccee4c986aa9ed73d3deab3145f43689aa58ee4
treec62c9ccc4bf34780d03f9925cb6a7c07c39790d4
parent11d38a7e520f485206b7b010f64127d864194e4c

stage2: more progress towards mutable local variables

* implement sema for runtime deref, store pointer, coerce_to_ptr_elem, and store * identifiers support being lvalues, except for decls is still TODO * codegen supports load, store, ref, alloc * introduce more MCValue union tags to support pointers * add load, ref, store typed IR instructions * add Type.isVolatilePtr

7 files changed, 372 insertions(+), 65 deletions(-)

src-self-hosted/Module.zig+24-1
...@@ -2151,7 +2151,8 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr...@@ -2151,7 +2151,8 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr
2151 });2151 });
2152 }2152 }
21532153
2154 return self.fail(scope, src, "TODO implement runtime deref", .{});2154 const b = try self.requireRuntimeBlock(scope, src);
2155 return self.addUnOp(b, src, elem_ty, .load, ptr);
2155}2156}
21562157
2157pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {2158pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
...@@ -2504,6 +2505,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2504,6 +2505,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2504 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });2505 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
2505}2506}
25062507
2508pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
2509 if (ptr.ty.isConstPtr())
2510 return self.fail(scope, src, "cannot assign to constant", .{});
2511
2512 const elem_ty = ptr.ty.elemType();
2513 const value = try self.coerce(scope, elem_ty, uncasted_value);
2514 if (elem_ty.onePossibleValue())
2515 return self.constVoid(scope, src);
2516
2517 // TODO handle comptime pointer writes
2518 // TODO handle if the element type requires comptime
2519
2520 const b = try self.requireRuntimeBlock(scope, src);
2521 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
2522}
2523
2507pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {2524pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2508 if (inst.value()) |val| {2525 if (inst.value()) |val| {
2509 // Keep the comptime Value representation; take the new type.2526 // Keep the comptime Value representation; take the new type.
...@@ -2780,3 +2797,9 @@ pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type)...@@ -2780,3 +2797,9 @@ pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type)
2780 type_payload.* = .{ .pointee_type = elem_ty };2797 type_payload.* = .{ .pointee_type = elem_ty };
2781 return Type.initPayload(&type_payload.base);2798 return Type.initPayload(&type_payload.base);
2782}2799}
2800
2801pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2802 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2803 type_payload.* = .{ .pointee_type = elem_ty };
2804 return Type.initPayload(&type_payload.base);
2805}
src-self-hosted/astgen.zig+19-12
...@@ -87,7 +87,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -87,7 +87,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
87 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),87 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
88 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),88 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
8989
90 .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),90 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
91 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),91 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
92 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),92 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
93 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),93 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
...@@ -469,7 +469,7 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE...@@ -469,7 +469,7 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
469 }469 }
470}470}
471471
472fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst {472fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
473 const tracy = trace(@src());473 const tracy = trace(@src());
474 defer tracy.end();474 defer tracy.end();
475475
...@@ -481,7 +481,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -481,7 +481,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481 }481 }
482482
483 if (getSimplePrimitiveValue(ident_name)) |typed_value| {483 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
484 return addZIRInstConst(mod, scope, src, typed_value);484 const result = try addZIRInstConst(mod, scope, src, typed_value);
485 return rlWrap(mod, scope, rl, result);
485 }486 }
486487
487 if (ident_name.len >= 2) integer: {488 if (ident_name.len >= 2) integer: {
...@@ -505,16 +506,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -505,16 +506,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
505 else => {506 else => {
506 const int_type_payload = try scope.arena().create(Value.Payload.IntType);507 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
507 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };508 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
508 return addZIRInstConst(mod, scope, src, .{509 const result = try addZIRInstConst(mod, scope, src, .{
509 .ty = Type.initTag(.comptime_int),510 .ty = Type.initTag(.comptime_int),
510 .val = Value.initPayload(&int_type_payload.base),511 .val = Value.initPayload(&int_type_payload.base),
511 });512 });
513 return rlWrap(mod, scope, rl, result);
512 },514 },
513 };515 };
514 return addZIRInstConst(mod, scope, src, .{516 const result = try addZIRInstConst(mod, scope, src, .{
515 .ty = Type.initTag(.type),517 .ty = Type.initTag(.type),
516 .val = val,518 .val = val,
517 });519 });
520 return rlWrap(mod, scope, rl, result);
518 }521 }
519 }522 }
520523
...@@ -525,14 +528,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -525,14 +528,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
525 .local_val => {528 .local_val => {
526 const local_val = s.cast(Scope.LocalVal).?;529 const local_val = s.cast(Scope.LocalVal).?;
527 if (mem.eql(u8, local_val.name, ident_name)) {530 if (mem.eql(u8, local_val.name, ident_name)) {
528 return local_val.inst;531 return rlWrap(mod, scope, rl, local_val.inst);
529 }532 }
530 s = local_val.parent;533 s = local_val.parent;
531 },534 },
532 .local_ptr => {535 .local_ptr => {
533 const local_ptr = s.cast(Scope.LocalPtr).?;536 const local_ptr = s.cast(Scope.LocalPtr).?;
534 if (mem.eql(u8, local_ptr.name, ident_name)) {537 if (mem.eql(u8, local_ptr.name, ident_name)) {
535 return try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);538 if (rl == .lvalue) {
539 return local_ptr.ptr;
540 } else {
541 const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
542 return rlWrap(mod, scope, rl, result);
543 }
536 }544 }
537 s = local_ptr.parent;545 s = local_ptr.parent;
538 },546 },
...@@ -542,7 +550,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -542,7 +550,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
542 }550 }
543551
544 if (mod.lookupDeclName(scope, ident_name)) |decl| {552 if (mod.lookupDeclName(scope, ident_name)) |decl| {
545 return try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});553 // TODO handle lvalues
554 const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
555 return rlWrap(mod, scope, rl, result);
546 }556 }
547557
548 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});558 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
...@@ -1066,10 +1076,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1066,10 +1076,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1066 .ptr = ptr_inst,1076 .ptr = ptr_inst,
1067 .value = result,1077 .value = result,
1068 }, .{});1078 }, .{});
1069 _ = try addZIRInst(mod, scope, result.src, zir.Inst.Store, .{1079 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
1070 .ptr = ptr_inst,
1071 .value = casted_result,
1072 }, .{});
1073 return casted_result;1080 return casted_result;
1074 },1081 },
1075 .bitcasted_ptr => |bitcasted_ptr| {1082 .bitcasted_ptr => |bitcasted_ptr| {
src-self-hosted/codegen.zig+253-36
...@@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
209 err_msg: ?*ErrorMsg,209 err_msg: ?*ErrorMsg,
210 args: []MCValue,210 args: []MCValue,
211 ret_mcv: MCValue,211 ret_mcv: MCValue,
212 fn_type: Type,
212 arg_index: usize,213 arg_index: usize,
213 src: usize,214 src: usize,
214 stack_align: u32,215 stack_align: u32,
...@@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
230 /// No more references to this value remain.231 /// No more references to this value remain.
231 dead,232 dead,
232 /// A pointer-sized integer that fits in a register.233 /// A pointer-sized integer that fits in a register.
234 /// If the type is a pointer, this is the pointer address in virtual address space.
233 immediate: u64,235 immediate: u64,
234 /// The constant was emitted into the code, at this offset.236 /// The constant was emitted into the code, at this offset.
237 /// If the type is a pointer, it means the pointer address is embedded in the code.
235 embedded_in_code: usize,238 embedded_in_code: usize,
239 /// The value is a pointer to a constant which was emitted into the code, at this offset.
240 ptr_embedded_in_code: usize,
236 /// The value is in a target-specific register.241 /// The value is in a target-specific register.
237 register: Register,242 register: Register,
238 /// The value is in memory at a hard-coded address.243 /// The value is in memory at a hard-coded address.
244 /// If the type is a pointer, it means the pointer address is at this memory location.
239 memory: u64,245 memory: u64,
240 /// The value is one of the stack variables.246 /// The value is one of the stack variables.
241 stack_offset: u64,247 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
248 stack_offset: u32,
249 /// The value is a pointer to one of the stack variables (payload is stack offset).
250 ptr_stack_offset: u32,
242 /// The value is in the compare flags assuming an unsigned operation,251 /// The value is in the compare flags assuming an unsigned operation,
243 /// with this operator applied on top of it.252 /// with this operator applied on top of it.
244 compare_flags_unsigned: math.CompareOperator,253 compare_flags_unsigned: math.CompareOperator,
...@@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
271 .memory,280 .memory,
272 .compare_flags_unsigned,281 .compare_flags_unsigned,
273 .compare_flags_signed,282 .compare_flags_signed,
283 .ptr_stack_offset,
284 .ptr_embedded_in_code,
274 => false,285 => false,
275286
276 .register,287 .register,
...@@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
356 .err_msg = null,367 .err_msg = null,
357 .args = undefined, // populated after `resolveCallingConventionValues`368 .args = undefined, // populated after `resolveCallingConventionValues`
358 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`369 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
370 .fn_type = fn_type,
359 .arg_index = 0,371 .arg_index = 0,
360 .branch_stack = &branch_stack,372 .branch_stack = &branch_stack,
361 .src = src,373 .src = src,
...@@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
459 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),471 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
460 .condbr => return self.genCondBr(inst.castTag(.condbr).?),472 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
461 .constant => unreachable, // excluded from function bodies473 .constant => unreachable, // excluded from function bodies
474 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
475 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
462 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),476 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
463 .isnull => return self.genIsNull(inst.castTag(.isnull).?),477 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
478 .load => return self.genLoad(inst.castTag(.load).?),
479 .not => return self.genNot(inst.castTag(.not).?),
464 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),480 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
481 .ref => return self.genRef(inst.castTag(.ref).?),
465 .ret => return self.genRet(inst.castTag(.ret).?),482 .ret => return self.genRet(inst.castTag(.ret).?),
466 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),483 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
484 .store => return self.genStore(inst.castTag(.store).?),
467 .sub => return self.genSub(inst.castTag(.sub).?),485 .sub => return self.genSub(inst.castTag(.sub).?),
468 .unreach => return MCValue{ .unreach = {} },486 .unreach => return MCValue{ .unreach = {} },
469 .not => return self.genNot(inst.castTag(.not).?),
470 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
471 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
472 }487 }
473 }488 }
474489
475 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {490 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
476 const elem_ty = inst.base.ty.elemType();
477 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
478 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{elem_ty});
479 };
480 // TODO swap this for inst.base.ty.ptrAlign
481 const abi_align = elem_ty.abiAlignment(self.target.*);
482 if (abi_align > self.stack_align)491 if (abi_align > self.stack_align)
483 self.stack_align = abi_align;492 self.stack_align = abi_align;
484 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];493 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -488,10 +497,66 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -488,10 +497,66 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
488 if (branch.next_stack_offset > branch.max_end_stack)497 if (branch.next_stack_offset > branch.max_end_stack)
489 branch.max_end_stack = branch.next_stack_offset;498 branch.max_end_stack = branch.next_stack_offset;
490 try branch.stack.putNoClobber(self.gpa, offset, .{499 try branch.stack.putNoClobber(self.gpa, offset, .{
491 .inst = &inst.base,500 .inst = inst,
492 .size = abi_size,501 .size = abi_size,
493 });502 });
494 return MCValue{ .stack_offset = offset };503 return offset;
504 }
505
506 /// Use a pointer instruction as the basis for allocating stack memory.
507 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
508 const elem_ty = inst.ty.elemType();
509 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
510 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
511 };
512 // TODO swap this for inst.ty.ptrAlign
513 const abi_align = elem_ty.abiAlignment(self.target.*);
514 return self.allocMem(inst, abi_size, abi_align);
515 }
516
517 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {
518 const elem_ty = inst.ty;
519 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
520 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
521 };
522 const abi_align = elem_ty.abiAlignment(self.target.*);
523 if (abi_align > self.stack_align)
524 self.stack_align = abi_align;
525 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
526
527 // TODO Make sure the type can fit in a register before we try to allocate one.
528 const free_index = @ctz(FreeRegInt, branch.free_registers);
529 if (free_index >= callee_preserved_regs.len) {
530 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
531 return MCValue{ .stack_offset = stack_offset };
532 }
533 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
534 const reg = callee_preserved_regs[free_index];
535 try branch.registers.putNoClobber(self.gpa, reg, .{ .inst = inst });
536 return MCValue{ .register = reg };
537 }
538
539 /// Does not "move" the instruction.
540 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
541 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
542 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
543 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
544
545 const free_index = @ctz(FreeRegInt, branch.free_registers);
546 if (free_index >= callee_preserved_regs.len)
547 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
548 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
549 const reg = callee_preserved_regs[free_index];
550 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
551 const old_mcv = branch.inst_table.get(inst).?;
552 const new_mcv: MCValue = .{ .register = reg };
553 try self.genSetReg(inst.src, reg, old_mcv);
554 return new_mcv;
555 }
556
557 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
558 const stack_offset = try self.allocMemPtr(&inst.base);
559 return MCValue{ .ptr_stack_offset = stack_offset };
495 }560 }
496561
497 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {562 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
...@@ -572,6 +637,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -572,6 +637,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
572 }637 }
573 }638 }
574639
640 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
641 const elem_ty = inst.base.ty;
642 if (!elem_ty.hasCodeGenBits())
643 return MCValue.none;
644 const ptr = try self.resolveInst(inst.operand);
645 const is_volatile = inst.operand.ty.isVolatilePtr();
646 if (inst.base.isUnused() and !is_volatile)
647 return MCValue.dead;
648 const dst_mcv: MCValue = blk: {
649 if (inst.base.operandDies(0) and ptr.isMutable()) {
650 // The MCValue that holds the pointer can be re-used as the value.
651 // TODO track this in the register/stack allocation metadata.
652 break :blk ptr;
653 } else {
654 break :blk try self.allocRegOrMem(&inst.base);
655 }
656 };
657 switch (ptr) {
658 .none => unreachable,
659 .unreach => unreachable,
660 .dead => unreachable,
661 .compare_flags_unsigned => unreachable,
662 .compare_flags_signed => unreachable,
663 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
664 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
665 .ptr_embedded_in_code => |off| {
666 try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
667 },
668 .embedded_in_code => {
669 return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
670 },
671 .register => {
672 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
673 },
674 .memory => {
675 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
676 },
677 .stack_offset => {
678 return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
679 },
680 }
681 return dst_mcv;
682 }
683
684 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
685 const ptr = try self.resolveInst(inst.lhs);
686 const value = try self.resolveInst(inst.rhs);
687 const elem_ty = inst.rhs.ty;
688 switch (ptr) {
689 .none => unreachable,
690 .unreach => unreachable,
691 .dead => unreachable,
692 .compare_flags_unsigned => unreachable,
693 .compare_flags_signed => unreachable,
694 .immediate => |imm| {
695 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
696 },
697 .ptr_stack_offset => |off| {
698 try self.genSetStack(inst.base.src, elem_ty, off, value);
699 },
700 .ptr_embedded_in_code => |off| {
701 try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
702 },
703 .embedded_in_code => {
704 return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
705 },
706 .register => {
707 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
708 },
709 .memory => {
710 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
711 },
712 .stack_offset => {
713 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
714 },
715 }
716 return .none;
717 }
718
575 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {719 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
576 // No side effects, so if it's unreferenced, do nothing.720 // No side effects, so if it's unreferenced, do nothing.
577 if (inst.base.isUnused())721 if (inst.base.isUnused())
...@@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
657 .dead, .unreach, .immediate => unreachable,801 .dead, .unreach, .immediate => unreachable,
658 .compare_flags_unsigned => unreachable,802 .compare_flags_unsigned => unreachable,
659 .compare_flags_signed => unreachable,803 .compare_flags_signed => unreachable,
804 .ptr_stack_offset => unreachable,
805 .ptr_embedded_in_code => unreachable,
660 .register => |dst_reg| {806 .register => |dst_reg| {
661 switch (src_mcv) {807 switch (src_mcv) {
662 .none => unreachable,808 .none => unreachable,
663 .dead, .unreach => unreachable,809 .dead, .unreach => unreachable,
810 .ptr_stack_offset => unreachable,
811 .ptr_embedded_in_code => unreachable,
664 .register => |src_reg| {812 .register => |src_reg| {
665 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });813 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
666 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });814 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
...@@ -743,6 +891,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -743,6 +891,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
743 for (info.args) |mc_arg, arg_i| {891 for (info.args) |mc_arg, arg_i| {
744 const arg = inst.args[arg_i];892 const arg = inst.args[arg_i];
745 const arg_mcv = try self.resolveInst(inst.args[arg_i]);893 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
894 // Here we do not use setRegOrMem even though the logic is similar, because
895 // the function call will move the stack pointer, so the offsets are different.
746 switch (mc_arg) {896 switch (mc_arg) {
747 .none => continue,897 .none => continue,
748 .register => |reg| {898 .register => |reg| {
...@@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
754 // mov qword ptr [rsp + stack_offset], x904 // mov qword ptr [rsp + stack_offset], x
755 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});905 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
756 },906 },
907 .ptr_stack_offset => {
908 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset", .{});
909 },
910 .ptr_embedded_in_code => {
911 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code", .{});
912 },
757 .immediate => unreachable,913 .immediate => unreachable,
758 .unreach => unreachable,914 .unreach => unreachable,
759 .dead => unreachable,915 .dead => unreachable,
...@@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
788 return info.return_value;944 return info.return_value;
789 }945 }
790946
947 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
948 const operand = try self.resolveInst(inst.operand);
949 switch (operand) {
950 .unreach => unreachable,
951 .dead => unreachable,
952 .none => return .none,
953
954 .immediate,
955 .register,
956 .ptr_stack_offset,
957 .ptr_embedded_in_code,
958 .compare_flags_unsigned,
959 .compare_flags_signed,
960 => {
961 const stack_offset = try self.allocMemPtr(&inst.base);
962 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
963 return MCValue{ .ptr_stack_offset = stack_offset };
964 },
965
966 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
967 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
968 .memory => |vaddr| return MCValue{ .immediate = vaddr },
969 }
970 }
971
791 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {972 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
792 try self.setRegOrStack(src, self.ret_mcv, mcv);973 const ret_ty = self.fn_type.fnReturnType();
974 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
793 switch (arch) {975 switch (arch) {
794 .i386 => {976 .i386 => {
795 try self.code.append(0xc3); // ret977 try self.code.append(0xc3); // ret
...@@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1042 }1224 }
10431225
1044 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.1226 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1045 fn setRegOrStack(self: *Self, src: usize, loc: MCValue, val: MCValue) !void {1227 fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
1046 switch (loc) {1228 switch (loc) {
1047 .none => return,1229 .none => return,
1048 .register => |reg| return self.genSetReg(src, reg, val),1230 .register => |reg| return self.genSetReg(src, reg, val),
1049 .stack_offset => {1231 .stack_offset => |off| return self.genSetStack(src, ty, off, val),
1050 return self.fail(src, "TODO implement setRegOrStack for stack offset", .{});1232 .memory => {
1233 return self.fail(src, "TODO implement setRegOrMem for memory", .{});
1051 },1234 },
1052 else => unreachable,1235 else => unreachable,
1053 }1236 }
1054 }1237 }
10551238
1056 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {1239 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1240 switch (arch) {
1241 .x86_64 => switch (mcv) {
1242 .dead => unreachable,
1243 .ptr_stack_offset => unreachable,
1244 .ptr_embedded_in_code => unreachable,
1245 .unreach, .none => return, // Nothing to do.
1246 .compare_flags_unsigned => |op| {
1247 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
1248 },
1249 .compare_flags_signed => |op| {
1250 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
1251 },
1252 .immediate => |x_big| {
1253 try self.code.ensureCapacity(self.code.items.len + 7);
1254 if (x_big <= math.maxInt(u32)) {
1255 const x = @intCast(u32, x_big);
1256 if (stack_offset > 128) {
1257 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
1258 }
1259 // We have a positive stack offset value but we want a twos complement negative
1260 // offset from rbp, which is at the top of the stack frame.
1261 const negative_offset = @intCast(i8, -@intCast(i32, stack_offset));
1262 const twos_comp = @bitCast(u8, negative_offset);
1263 // mov DWORD PTR [rbp+offset], immediate
1264 self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
1265 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
1266 } else {
1267 return self.fail(src, "TODO implement set stack variable with large immediate", .{});
1268 }
1269 },
1270 .embedded_in_code => |code_offset| {
1271 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
1272 },
1273 .register => |reg| {
1274 return self.fail(src, "TODO implement set stack variable from register", .{});
1275 },
1276 .memory => |vaddr| {
1277 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
1278 },
1279 .stack_offset => |off| {
1280 if (stack_offset == off)
1281 return; // Copy stack variable to itself; nothing to do.
1282 return self.fail(src, "TODO implement copy stack variable to stack variable", .{});
1283 },
1284 },
1285 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
1286 }
1287 }
1288
1289 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1057 switch (arch) {1290 switch (arch) {
1058 .x86_64 => switch (mcv) {1291 .x86_64 => switch (mcv) {
1059 .dead => unreachable,1292 .dead => unreachable,
1293 .ptr_stack_offset => unreachable,
1294 .ptr_embedded_in_code => unreachable,
1060 .unreach, .none => return, // Nothing to do.1295 .unreach, .none => return, // Nothing to do.
1061 .compare_flags_unsigned => |op| {1296 .compare_flags_unsigned => |op| {
1062 try self.code.ensureCapacity(self.code.items.len + 3);1297 try self.code.ensureCapacity(self.code.items.len + 3);
...@@ -1279,24 +1514,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1279,24 +1514,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1279 }1514 }
1280 }1515 }
12811516
1282 /// Does not "move" the instruction.
1283 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1284 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1285 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1286 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
1287
1288 const free_index = @ctz(FreeRegInt, branch.free_registers);
1289 if (free_index >= callee_preserved_regs.len)
1290 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
1291 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
1292 const reg = callee_preserved_regs[free_index];
1293 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
1294 const old_mcv = branch.inst_table.get(inst).?;
1295 const new_mcv: MCValue = .{ .register = reg };
1296 try self.genSetReg(inst.src, reg, old_mcv);
1297 return new_mcv;
1298 }
1299
1300 /// If the MCValue is an immediate, and it does not fit within this type,1517 /// If the MCValue is an immediate, and it does not fit within this type,
1301 /// we put it in a register.1518 /// we put it in a register.
1302 /// A potential opportunity for future optimization here would be keeping track1519 /// A potential opportunity for future optimization here would be keeping track
src-self-hosted/ir.zig+8
...@@ -67,9 +67,14 @@ pub const Inst = struct {...@@ -67,9 +67,14 @@ pub const Inst = struct {
67 constant,67 constant,
68 isnonnull,68 isnonnull,
69 isnull,69 isnull,
70 /// Read a value from a pointer.
71 load,
70 ptrtoint,72 ptrtoint,
73 ref,
71 ret,74 ret,
72 retvoid,75 retvoid,
76 /// Write a value to a pointer. LHS is pointer, RHS is value.
77 store,
73 sub,78 sub,
74 unreach,79 unreach,
75 not,80 not,
...@@ -85,6 +90,7 @@ pub const Inst = struct {...@@ -85,6 +90,7 @@ pub const Inst = struct {
85 .breakpoint,90 .breakpoint,
86 => NoOp,91 => NoOp,
8792
93 .ref,
88 .ret,94 .ret,
89 .bitcast,95 .bitcast,
90 .not,96 .not,
...@@ -93,6 +99,7 @@ pub const Inst = struct {...@@ -93,6 +99,7 @@ pub const Inst = struct {
93 .ptrtoint,99 .ptrtoint,
94 .floatcast,100 .floatcast,
95 .intcast,101 .intcast,
102 .load,
96 => UnOp,103 => UnOp,
97104
98 .add,105 .add,
...@@ -103,6 +110,7 @@ pub const Inst = struct {...@@ -103,6 +110,7 @@ pub const Inst = struct {
103 .cmp_gte,110 .cmp_gte,
104 .cmp_gt,111 .cmp_gt,
105 .cmp_neq,112 .cmp_neq,
113 .store,
106 => BinOp,114 => BinOp,
107115
108 .assembly => Assembly,116 .assembly => Assembly,
src-self-hosted/type.zig+52
...@@ -803,6 +803,58 @@ pub const Type = extern union {...@@ -803,6 +803,58 @@ pub const Type = extern union {
803 };803 };
804 }804 }
805805
806 pub fn isVolatilePtr(self: Type) bool {
807 return switch (self.tag()) {
808 .u8,
809 .i8,
810 .u16,
811 .i16,
812 .u32,
813 .i32,
814 .u64,
815 .i64,
816 .usize,
817 .isize,
818 .c_short,
819 .c_ushort,
820 .c_int,
821 .c_uint,
822 .c_long,
823 .c_ulong,
824 .c_longlong,
825 .c_ulonglong,
826 .c_longdouble,
827 .f16,
828 .f32,
829 .f64,
830 .f128,
831 .c_void,
832 .bool,
833 .void,
834 .type,
835 .anyerror,
836 .comptime_int,
837 .comptime_float,
838 .noreturn,
839 .@"null",
840 .@"undefined",
841 .array,
842 .array_u8_sentinel_0,
843 .fn_noreturn_no_args,
844 .fn_void_no_args,
845 .fn_naked_noreturn_no_args,
846 .fn_ccc_void_no_args,
847 .function,
848 .int_unsigned,
849 .int_signed,
850 .single_mut_pointer,
851 .single_const_pointer,
852 .single_const_pointer_to_comptime_int,
853 .const_slice_u8,
854 => false,
855 };
856 }
857
806 /// Asserts the type is a pointer or array type.858 /// Asserts the type is a pointer or array type.
807 pub fn elemType(self: Type) Type {859 pub fn elemType(self: Type) Type {
808 return switch (self.tag()) {860 return switch (self.tag()) {
src-self-hosted/zir.zig+4-12
...@@ -242,6 +242,7 @@ pub const Inst = struct {...@@ -242,6 +242,7 @@ pub const Inst = struct {
242 .mulwrap,242 .mulwrap,
243 .shl,243 .shl,
244 .shr,244 .shr,
245 .store,
245 .sub,246 .sub,
246 .subwrap,247 .subwrap,
247 .cmp_lt,248 .cmp_lt,
...@@ -270,7 +271,6 @@ pub const Inst = struct {...@@ -270,7 +271,6 @@ pub const Inst = struct {
270 .coerce_result_block_ptr => CoerceResultBlockPtr,271 .coerce_result_block_ptr => CoerceResultBlockPtr,
271 .compileerror => CompileError,272 .compileerror => CompileError,
272 .@"const" => Const,273 .@"const" => Const,
273 .store => Store,
274 .str => Str,274 .str => Str,
275 .int => Int,275 .int => Int,
276 .inttype => IntType,276 .inttype => IntType,
...@@ -545,17 +545,6 @@ pub const Inst = struct {...@@ -545,17 +545,6 @@ pub const Inst = struct {
545 kw_args: struct {},545 kw_args: struct {},
546 };546 };
547547
548 pub const Store = struct {
549 pub const base_tag = Tag.store;
550 base: Inst,
551
552 positionals: struct {
553 ptr: *Inst,
554 value: *Inst,
555 },
556 kw_args: struct {},
557 };
558
559 pub const Str = struct {548 pub const Str = struct {
560 pub const base_tag = Tag.str;549 pub const base_tag = Tag.str;
561 base: Inst,550 base: Inst,
...@@ -1837,9 +1826,12 @@ const EmitZIR = struct {...@@ -1837,9 +1826,12 @@ const EmitZIR = struct {
1837 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),1826 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
1838 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),1827 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
1839 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),1828 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
1829 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
1830 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
18401831
1841 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),1832 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1842 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),1833 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
1834 .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
1843 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),1835 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
1844 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),1836 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
1845 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),1837 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
src-self-hosted/zir_sema.zig+12-4
...@@ -287,8 +287,11 @@ fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp...@@ -287,8 +287,11 @@ fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp
287 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});287 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
288}288}
289289
290/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
290fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {291fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
291 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});292 const ptr = try resolveInst(mod, scope, inst.positionals.ptr);
293 const operand = try resolveInst(mod, scope, inst.positionals.value);
294 return mod.coerce(scope, ptr.ty.elemType(), operand);
292}295}
293296
294fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {297fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
...@@ -296,7 +299,10 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr...@@ -296,7 +299,10 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
296}299}
297300
298fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {301fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
299 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{});302 const operand = try resolveInst(mod, scope, inst.positionals.operand);
303 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
304 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);
305 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
300}306}
301307
302fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {308fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
...@@ -333,8 +339,10 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I...@@ -333,8 +339,10 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I
333 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});339 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
334}340}
335341
336fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {342fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
337 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});343 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
344 const value = try resolveInst(mod, scope, inst.positionals.rhs);
345 return mod.storePtr(scope, inst.base.src, ptr, value);
338}346}
339347
340fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {348fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {