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
21512151 });
21522152 }
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);
21552156}
21562157
21572158pub 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
25042505 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
25052506}
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
25072524pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
25082525 if (inst.value()) |val| {
25092526 // 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)
27802797 type_payload.* = .{ .pointee_type = elem_ty };
27812798 return Type.initPayload(&type_payload.base);
27822799}
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
8787 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
8888 .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).?),
9191 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
9292 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
9393 .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
469469 }
470470}
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 {
473473 const tracy = trace(@src());
474474 defer tracy.end();
475475
......@@ -481,7 +481,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481481 }
482482
483483 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);
485486 }
486487
487488 if (ident_name.len >= 2) integer: {
......@@ -505,16 +506,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
505506 else => {
506507 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
507508 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
508 return addZIRInstConst(mod, scope, src, .{
509 const result = try addZIRInstConst(mod, scope, src, .{
509510 .ty = Type.initTag(.comptime_int),
510511 .val = Value.initPayload(&int_type_payload.base),
511512 });
513 return rlWrap(mod, scope, rl, result);
512514 },
513515 };
514 return addZIRInstConst(mod, scope, src, .{
516 const result = try addZIRInstConst(mod, scope, src, .{
515517 .ty = Type.initTag(.type),
516518 .val = val,
517519 });
520 return rlWrap(mod, scope, rl, result);
518521 }
519522 }
520523
......@@ -525,14 +528,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
525528 .local_val => {
526529 const local_val = s.cast(Scope.LocalVal).?;
527530 if (mem.eql(u8, local_val.name, ident_name)) {
528 return local_val.inst;
531 return rlWrap(mod, scope, rl, local_val.inst);
529532 }
530533 s = local_val.parent;
531534 },
532535 .local_ptr => {
533536 const local_ptr = s.cast(Scope.LocalPtr).?;
534537 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 }
536544 }
537545 s = local_ptr.parent;
538546 },
......@@ -542,7 +550,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
542550 }
543551
544552 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);
546556 }
547557
548558 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
10661076 .ptr = ptr_inst,
10671077 .value = result,
10681078 }, .{});
1069 _ = try addZIRInst(mod, scope, result.src, zir.Inst.Store, .{
1070 .ptr = ptr_inst,
1071 .value = casted_result,
1072 }, .{});
1079 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
10731080 return casted_result;
10741081 },
10751082 .bitcasted_ptr => |bitcasted_ptr| {
src-self-hosted/codegen.zig+253-36
......@@ -209,6 +209,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
209209 err_msg: ?*ErrorMsg,
210210 args: []MCValue,
211211 ret_mcv: MCValue,
212 fn_type: Type,
212213 arg_index: usize,
213214 src: usize,
214215 stack_align: u32,
......@@ -230,15 +231,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
230231 /// No more references to this value remain.
231232 dead,
232233 /// 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.
233235 immediate: u64,
234236 /// 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.
235238 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,
236241 /// The value is in a target-specific register.
237242 register: Register,
238243 /// 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.
239245 memory: u64,
240246 /// 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,
242251 /// The value is in the compare flags assuming an unsigned operation,
243252 /// with this operator applied on top of it.
244253 compare_flags_unsigned: math.CompareOperator,
......@@ -271,6 +280,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
271280 .memory,
272281 .compare_flags_unsigned,
273282 .compare_flags_signed,
283 .ptr_stack_offset,
284 .ptr_embedded_in_code,
274285 => false,
275286
276287 .register,
......@@ -356,6 +367,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
356367 .err_msg = null,
357368 .args = undefined, // populated after `resolveCallingConventionValues`
358369 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
370 .fn_type = fn_type,
359371 .arg_index = 0,
360372 .branch_stack = &branch_stack,
361373 .src = src,
......@@ -459,26 +471,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
459471 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
460472 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
461473 .constant => unreachable, // excluded from function bodies
474 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
475 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
462476 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
463477 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
478 .load => return self.genLoad(inst.castTag(.load).?),
479 .not => return self.genNot(inst.castTag(.not).?),
464480 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
481 .ref => return self.genRef(inst.castTag(.ref).?),
465482 .ret => return self.genRet(inst.castTag(.ret).?),
466483 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
484 .store => return self.genStore(inst.castTag(.store).?),
467485 .sub => return self.genSub(inst.castTag(.sub).?),
468486 .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).?),
472487 }
473488 }
474489
475 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
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.*);
490 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
482491 if (abi_align > self.stack_align)
483492 self.stack_align = abi_align;
484493 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 {
488497 if (branch.next_stack_offset > branch.max_end_stack)
489498 branch.max_end_stack = branch.next_stack_offset;
490499 try branch.stack.putNoClobber(self.gpa, offset, .{
491 .inst = &inst.base,
500 .inst = inst,
492501 .size = abi_size,
493502 });
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 };
495560 }
496561
497562 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
......@@ -572,6 +637,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
572637 }
573638 }
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
575719 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
576720 // No side effects, so if it's unreferenced, do nothing.
577721 if (inst.base.isUnused())
......@@ -657,10 +801,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
657801 .dead, .unreach, .immediate => unreachable,
658802 .compare_flags_unsigned => unreachable,
659803 .compare_flags_signed => unreachable,
804 .ptr_stack_offset => unreachable,
805 .ptr_embedded_in_code => unreachable,
660806 .register => |dst_reg| {
661807 switch (src_mcv) {
662808 .none => unreachable,
663809 .dead, .unreach => unreachable,
810 .ptr_stack_offset => unreachable,
811 .ptr_embedded_in_code => unreachable,
664812 .register => |src_reg| {
665813 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
666814 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 {
743891 for (info.args) |mc_arg, arg_i| {
744892 const arg = inst.args[arg_i];
745893 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.
746896 switch (mc_arg) {
747897 .none => continue,
748898 .register => |reg| {
......@@ -754,6 +904,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
754904 // mov qword ptr [rsp + stack_offset], x
755905 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
756906 },
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 },
757913 .immediate => unreachable,
758914 .unreach => unreachable,
759915 .dead => unreachable,
......@@ -788,8 +944,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
788944 return info.return_value;
789945 }
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
791972 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);
793975 switch (arch) {
794976 .i386 => {
795977 try self.code.append(0xc3); // ret
......@@ -1042,21 +1224,74 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10421224 }
10431225
10441226 /// 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 {
10461228 switch (loc) {
10471229 .none => return,
10481230 .register => |reg| return self.genSetReg(src, reg, val),
1049 .stack_offset => {
1050 return self.fail(src, "TODO implement setRegOrStack for stack offset", .{});
1231 .stack_offset => |off| return self.genSetStack(src, ty, off, val),
1232 .memory => {
1233 return self.fail(src, "TODO implement setRegOrMem for memory", .{});
10511234 },
10521235 else => unreachable,
10531236 }
10541237 }
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 {
10571290 switch (arch) {
10581291 .x86_64 => switch (mcv) {
10591292 .dead => unreachable,
1293 .ptr_stack_offset => unreachable,
1294 .ptr_embedded_in_code => unreachable,
10601295 .unreach, .none => return, // Nothing to do.
10611296 .compare_flags_unsigned => |op| {
10621297 try self.code.ensureCapacity(self.code.items.len + 3);
......@@ -1279,24 +1514,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
12791514 }
12801515 }
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
13001517 /// If the MCValue is an immediate, and it does not fit within this type,
13011518 /// we put it in a register.
13021519 /// 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 {
6767 constant,
6868 isnonnull,
6969 isnull,
70 /// Read a value from a pointer.
71 load,
7072 ptrtoint,
73 ref,
7174 ret,
7275 retvoid,
76 /// Write a value to a pointer. LHS is pointer, RHS is value.
77 store,
7378 sub,
7479 unreach,
7580 not,
......@@ -85,6 +90,7 @@ pub const Inst = struct {
8590 .breakpoint,
8691 => NoOp,
8792
93 .ref,
8894 .ret,
8995 .bitcast,
9096 .not,
......@@ -93,6 +99,7 @@ pub const Inst = struct {
9399 .ptrtoint,
94100 .floatcast,
95101 .intcast,
102 .load,
96103 => UnOp,
97104
98105 .add,
......@@ -103,6 +110,7 @@ pub const Inst = struct {
103110 .cmp_gte,
104111 .cmp_gt,
105112 .cmp_neq,
113 .store,
106114 => BinOp,
107115
108116 .assembly => Assembly,
src-self-hosted/type.zig+52
......@@ -803,6 +803,58 @@ pub const Type = extern union {
803803 };
804804 }
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
806858 /// Asserts the type is a pointer or array type.
807859 pub fn elemType(self: Type) Type {
808860 return switch (self.tag()) {
src-self-hosted/zir.zig+4-12
......@@ -242,6 +242,7 @@ pub const Inst = struct {
242242 .mulwrap,
243243 .shl,
244244 .shr,
245 .store,
245246 .sub,
246247 .subwrap,
247248 .cmp_lt,
......@@ -270,7 +271,6 @@ pub const Inst = struct {
270271 .coerce_result_block_ptr => CoerceResultBlockPtr,
271272 .compileerror => CompileError,
272273 .@"const" => Const,
273 .store => Store,
274274 .str => Str,
275275 .int => Int,
276276 .inttype => IntType,
......@@ -545,17 +545,6 @@ pub const Inst = struct {
545545 kw_args: struct {},
546546 };
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
559548 pub const Str = struct {
560549 pub const base_tag = Tag.str;
561550 base: Inst,
......@@ -1837,9 +1826,12 @@ const EmitZIR = struct {
18371826 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
18381827 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
18391828 .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
18411832 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
18421833 .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),
18431835 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
18441836 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
18451837 .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
287287 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
288288}
289289
290/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
290291fn 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);
292295}
293296
294297fn 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
296299}
297300
298301fn 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);
300306}
301307
302308fn 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
333339 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
334340}
335341
336fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {
337 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});
342fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
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);
338346}
339347
340348fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {