authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-11 23:38:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-20 12:19:16-07:00
logee6432537ee29485c5de6c8b0911ef1482d752a7
tree76c65539c8f7a52ad1aa0754fd807998a433953b
parentef7080aed1a1a4dc54cb837938e462b4e6720734

stage2: first pass over codegen.zig for AIR memory layout


4 files changed, 769 insertions(+), 666 deletions(-)

BRANCH_TODO-38
...@@ -1,48 +1,10 @@...@@ -1,48 +1,10 @@
1 * be sure to test debug info of parameters1 * be sure to test debug info of parameters
22
33
4 pub fn isUnused(self: Inst) bool {
5 return (self.deaths & (1 << unreferenced_bit_index)) != 0;
6 }
7
8 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool {
9 assert(index < deaths_bits);
10 return @truncate(u1, self.deaths >> index) != 0;
11 }
12
13 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void {
14 assert(index < deaths_bits);
15 self.deaths &= ~(@as(DeathsInt, 1) << index);
16 }
17
18 pub fn specialOperandDeaths(self: Inst) bool {4 pub fn specialOperandDeaths(self: Inst) bool {
19 return (self.deaths & (1 << deaths_bits)) != 0;5 return (self.deaths & (1 << deaths_bits)) != 0;
20 }6 }
217
22 pub fn operandCount(base: *Inst) usize {
23 inline for (@typeInfo(Tag).Enum.fields) |field| {
24 const tag = @intToEnum(Tag, field.value);
25 if (tag == base.tag) {
26 return @fieldParentPtr(tag.Type(), "base", base).operandCount();
27 }
28 }
29 unreachable;
30 }
31
32 pub fn getOperand(base: *Inst, index: usize) ?*Inst {
33 inline for (@typeInfo(Tag).Enum.fields) |field| {
34 const tag = @intToEnum(Tag, field.value);
35 if (tag == base.tag) {
36 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index);
37 }
38 }
39 unreachable;
40 }
41
42 pub fn Args(comptime T: type) type {
43 return std.meta.fieldInfo(T, .args).field_type;
44 }
45
46 /// Returns `null` if runtime-known.8 /// Returns `null` if runtime-known.
47 /// Should be called by codegen, not by Sema. Sema functions should call9 /// Should be called by codegen, not by Sema. Sema functions should call
48 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.10 /// `resolvePossiblyUndefinedValue` or `resolveDefinedValue` instead.
src/Liveness.zig+21
...@@ -74,6 +74,26 @@ pub fn analyze(gpa: *Allocator, air: Air) Allocator.Error!Liveness {...@@ -74,6 +74,26 @@ pub fn analyze(gpa: *Allocator, air: Air) Allocator.Error!Liveness {
74 };74 };
75}75}
7676
77pub fn isUnused(l: Liveness, inst: Air.Inst.Index) bool {
78 const usize_index = (inst * bpi) / @bitSizeOf(usize);
79 const mask = @as(usize, 1) << ((inst % (@bitSizeOf(usize) / bpi)) * bpi + (bpi - 1));
80 return (l.tomb_bits[usize_index] & mask) != 0;
81}
82
83pub fn operandDies(l: Liveness, inst: Air.Inst.Index, operand: OperandInt) bool {
84 assert(operand < bpi - 1);
85 const usize_index = (inst * bpi) / @bitSizeOf(usize);
86 const mask = @as(usize, 1) << ((inst % (@bitSizeOf(usize) / bpi)) * bpi + operand);
87 return (l.tomb_bits[usize_index] & mask) != 0;
88}
89
90pub fn clearOperandDeath(l: *Liveness, inst: Air.Inst.Index, operand: OperandInt) void {
91 assert(operand < bpi - 1);
92 const usize_index = (inst * bpi) / @bitSizeOf(usize);
93 const mask = @as(usize, 1) << ((inst % (@bitSizeOf(usize) / bpi)) * bpi + operand);
94 l.tomb_bits[usize_index] |= mask;
95}
96
77pub fn deinit(l: *Liveness, gpa: *Allocator) void {97pub fn deinit(l: *Liveness, gpa: *Allocator) void {
78 gpa.free(l.tomb_bits);98 gpa.free(l.tomb_bits);
79 gpa.free(l.extra);99 gpa.free(l.extra);
...@@ -83,6 +103,7 @@ pub fn deinit(l: *Liveness, gpa: *Allocator) void {...@@ -83,6 +103,7 @@ pub fn deinit(l: *Liveness, gpa: *Allocator) void {
83/// How many tomb bits per AIR instruction.103/// How many tomb bits per AIR instruction.
84const bpi = 4;104const bpi = 4;
85const Bpi = std.meta.Int(.unsigned, bpi);105const Bpi = std.meta.Int(.unsigned, bpi);
106const OperandInt = std.math.Log2Int(Bpi);
86107
87/// In-progress data; on successful analysis converted into `Liveness`.108/// In-progress data; on successful analysis converted into `Liveness`.
88const Analysis = struct {109const Analysis = struct {
src/codegen.zig+743-622
...@@ -722,16 +722,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -722,16 +722,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
722 }722 }
723723
724 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {724 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
725 for (body.instructions) |inst| {725 for (body) |inst| {
726 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(inst.deaths), inst.deaths));726 const tomb_bits = self.liveness.getTombBits(inst);
727 try self.ensureProcessDeathCapacity(@popCount(@TypeOf(tomb_bits), tomb_bits));
727728
728 const mcv = try self.genFuncInst(inst);729 const mcv = try self.genFuncInst(inst);
729 if (!inst.isUnused()) {730 if (!self.liveness.isUnused(inst)) {
730 log.debug("{*} => {}", .{ inst, mcv });731 log.debug("{} => {}", .{ inst, mcv });
731 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];732 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
732 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);733 try branch.inst_table.putNoClobber(self.gpa, inst, mcv);
733 }734 }
734735
736 // TODO inline this logic into every instruction
735 var i: ir.Inst.DeathsBitIndex = 0;737 var i: ir.Inst.DeathsBitIndex = 0;
736 while (inst.getOperand(i)) |operand| : (i += 1) {738 while (inst.getOperand(i)) |operand| : (i += 1) {
737 if (inst.operandDies(i))739 if (inst.operandDies(i))
...@@ -785,8 +787,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -785,8 +787,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
785 }787 }
786788
787 /// Asserts there is already capacity to insert into top branch inst_table.789 /// Asserts there is already capacity to insert into top branch inst_table.
788 fn processDeath(self: *Self, inst: *ir.Inst) void {790 fn processDeath(self: *Self, inst: Air.Inst.Index) void {
789 if (inst.tag == .constant) return; // Constants are immortal.791 const air_tags = self.air.instructions.items(.tag);
792 if (air_tags[inst] == .constant) return; // Constants are immortal.
790 // When editing this function, note that the logic must synchronize with `reuseOperand`.793 // When editing this function, note that the logic must synchronize with `reuseOperand`.
791 const prev_value = self.getResolvedInstValue(inst);794 const prev_value = self.getResolvedInstValue(inst);
792 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];795 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -827,74 +830,82 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -827,74 +830,82 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
827 }830 }
828 }831 }
829832
830 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {833 fn genFuncInst(self: *Self, inst: Air.Inst.Index) !MCValue {
831 switch (inst.tag) {834 const air_tags = self.air.instructions.items(.tag);
832 .add => return self.genAdd(inst.castTag(.add).?),835 switch (air_tags[inst]) {
836 // zig fmt: off
837 .add => return self.genAdd(inst.castTag(.add).?),
833 .addwrap => return self.genAddWrap(inst.castTag(.addwrap).?),838 .addwrap => return self.genAddWrap(inst.castTag(.addwrap).?),
834 .alloc => return self.genAlloc(inst.castTag(.alloc).?),839 .sub => return self.genSub(inst.castTag(.sub).?),
835 .arg => return self.genArg(inst.castTag(.arg).?),840 .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?),
836 .assembly => return self.genAsm(inst.castTag(.assembly).?),841 .mul => return self.genMul(inst.castTag(.mul).?),
837 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),842 .mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?),
838 .bit_and => return self.genBitAnd(inst.castTag(.bit_and).?),843 .div => return self.genDiv(inst.castTag(.div).?),
839 .bit_or => return self.genBitOr(inst.castTag(.bit_or).?),844
840 .block => return self.genBlock(inst.castTag(.block).?),845 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
841 .br => return self.genBr(inst.castTag(.br).?),
842 .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),
843 .breakpoint => return self.genBreakpoint(inst.src),
844 .br_void => return self.genBrVoid(inst.castTag(.br_void).?),
845 .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),
846 .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),
847 .call => return self.genCall(inst.castTag(.call).?),
848 .cmp_lt => return self.genCmp(inst.castTag(.cmp_lt).?, .lt),
849 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),846 .cmp_lte => return self.genCmp(inst.castTag(.cmp_lte).?, .lte),
850 .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),847 .cmp_eq => return self.genCmp(inst.castTag(.cmp_eq).?, .eq),
851 .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),848 .cmp_gte => return self.genCmp(inst.castTag(.cmp_gte).?, .gte),
852 .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),849 .cmp_gt => return self.genCmp(inst.castTag(.cmp_gt).?, .gt),
853 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),850 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
854 .condbr => return self.genCondBr(inst.castTag(.condbr).?),851
855 .constant => unreachable, // excluded from function bodies852 .bool_and => return self.genBoolOp(inst.castTag(.bool_and).?),
856 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),853 .bool_or => return self.genBoolOp(inst.castTag(.bool_or).?),
857 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),854 .bit_and => return self.genBitAnd(inst.castTag(.bit_and).?),
858 .intcast => return self.genIntCast(inst.castTag(.intcast).?),855 .bit_or => return self.genBitOr(inst.castTag(.bit_or).?),
859 .is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?),856 .xor => return self.genXor(inst.castTag(.xor).?),
857
858 .alloc => return self.genAlloc(inst.castTag(.alloc).?),
859 .arg => return self.genArg(inst.castTag(.arg).?),
860 .assembly => return self.genAsm(inst.castTag(.assembly).?),
861 .bitcast => return self.genBitCast(inst.castTag(.bitcast).?),
862 .block => return self.genBlock(inst.castTag(.block).?),
863 .br => return self.genBr(inst.castTag(.br).?),
864 .br_block_flat => return self.genBrBlockFlat(inst.castTag(.br_block_flat).?),
865 .breakpoint => return self.genBreakpoint(inst.src),
866 .call => return self.genCall(inst.castTag(.call).?),
867 .cond_br => return self.genCondBr(inst.castTag(.condbr).?),
868 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
869 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
870 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
871 .is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?),
860 .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),872 .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
861 .is_null => return self.genIsNull(inst.castTag(.is_null).?),873 .is_null => return self.genIsNull(inst.castTag(.is_null).?),
862 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),874 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
863 .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),875 .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
864 .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),876 .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
865 .is_err => return self.genIsErr(inst.castTag(.is_err).?),877 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
866 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),878 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
867 .load => return self.genLoad(inst.castTag(.load).?),879 .load => return self.genLoad(inst.castTag(.load).?),
868 .loop => return self.genLoop(inst.castTag(.loop).?),880 .loop => return self.genLoop(inst.castTag(.loop).?),
869 .not => return self.genNot(inst.castTag(.not).?),881 .not => return self.genNot(inst.castTag(.not).?),
870 .mul => return self.genMul(inst.castTag(.mul).?),882 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
871 .mulwrap => return self.genMulWrap(inst.castTag(.mulwrap).?),883 .ref => return self.genRef(inst.castTag(.ref).?),
872 .div => return self.genDiv(inst.castTag(.div).?),884 .ret => return self.genRet(inst.castTag(.ret).?),
873 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),885 .store => return self.genStore(inst.castTag(.store).?),
874 .ref => return self.genRef(inst.castTag(.ref).?),886 .struct_field_ptr=> return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),
875 .ret => return self.genRet(inst.castTag(.ret).?),887 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
876 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),888 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
877 .store => return self.genStore(inst.castTag(.store).?),889
878 .struct_field_ptr => return self.genStructFieldPtr(inst.castTag(.struct_field_ptr).?),890 .constant => unreachable, // excluded from function bodies
879 .sub => return self.genSub(inst.castTag(.sub).?),891 .unreach => return MCValue{ .unreach = {} },
880 .subwrap => return self.genSubWrap(inst.castTag(.subwrap).?),892
881 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),893 .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),
882 .unreach => return MCValue{ .unreach = {} },894 .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),
883 .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),895 .unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),
884 .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),896 .unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),
885 .unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),897 .unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),
886 .unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),898 .unwrap_errunion_payload_ptr=> return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),
887 .unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),899
888 .unwrap_errunion_payload_ptr => return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),900 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
889 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
890 .wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),901 .wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
891 .wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),902 .wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),
892 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),903
893 .xor => return self.genXor(inst.castTag(.xor).?),904 // zig fmt: on
894 }905 }
895 }906 }
896907
897 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {908 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
898 if (abi_align > self.stack_align)909 if (abi_align > self.stack_align)
899 self.stack_align = abi_align;910 self.stack_align = abi_align;
900 // TODO find a free slot instead of always appending911 // TODO find a free slot instead of always appending
...@@ -910,20 +921,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -910,20 +921,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
910 }921 }
911922
912 /// Use a pointer instruction as the basis for allocating stack memory.923 /// Use a pointer instruction as the basis for allocating stack memory.
913 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {924 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
914 const elem_ty = inst.ty.elemType();925 const elem_ty = self.air.getType(inst).elemType();
915 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {926 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
916 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});927 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
917 };928 };
918 // TODO swap this for inst.ty.ptrAlign929 // TODO swap this for inst.ty.ptrAlign
919 const abi_align = elem_ty.abiAlignment(self.target.*);930 const abi_align = elem_ty.abiAlignment(self.target.*);
920 return self.allocMem(inst, abi_size, abi_align);931 return self.allocMem(inst, abi_size, abi_align);
921 }932 }
922933
923 fn allocRegOrMem(self: *Self, inst: *ir.Inst, reg_ok: bool) !MCValue {934 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
924 const elem_ty = inst.ty;935 const elem_ty = inst.ty;
925 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {936 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
926 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});937 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
927 };938 };
928 const abi_align = elem_ty.abiAlignment(self.target.*);939 const abi_align = elem_ty.abiAlignment(self.target.*);
929 if (abi_align > self.stack_align)940 if (abi_align > self.stack_align)
...@@ -943,72 +954,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -943,72 +954,75 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
943 return MCValue{ .stack_offset = stack_offset };954 return MCValue{ .stack_offset = stack_offset };
944 }955 }
945956
946 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {957 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
947 const stack_mcv = try self.allocRegOrMem(inst, false);958 const stack_mcv = try self.allocRegOrMem(inst, false);
948 log.debug("spilling {*} to stack mcv {any}", .{ inst, stack_mcv });959 log.debug("spilling {*} to stack mcv {any}", .{ inst, stack_mcv });
949 const reg_mcv = self.getResolvedInstValue(inst);960 const reg_mcv = self.getResolvedInstValue(inst);
950 assert(reg == toCanonicalReg(reg_mcv.register));961 assert(reg == toCanonicalReg(reg_mcv.register));
951 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];962 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
952 try branch.inst_table.put(self.gpa, inst, stack_mcv);963 try branch.inst_table.put(self.gpa, inst, stack_mcv);
953 try self.genSetStack(src, inst.ty, stack_mcv.stack_offset, reg_mcv);964 try self.genSetStack(inst.ty, stack_mcv.stack_offset, reg_mcv);
954 }965 }
955966
956 /// Copies a value to a register without tracking the register. The register is not considered967 /// Copies a value to a register without tracking the register. The register is not considered
957 /// allocated. A second call to `copyToTmpRegister` may return the same register.968 /// allocated. A second call to `copyToTmpRegister` may return the same register.
958 /// This can have a side effect of spilling instructions to the stack to free up a register.969 /// This can have a side effect of spilling instructions to the stack to free up a register.
959 fn copyToTmpRegister(self: *Self, src: LazySrcLoc, ty: Type, mcv: MCValue) !Register {970 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
960 const reg = try self.register_manager.allocReg(null, &.{});971 const reg = try self.register_manager.allocReg(null, &.{});
961 try self.genSetReg(src, ty, reg, mcv);972 try self.genSetReg(ty, reg, mcv);
962 return reg;973 return reg;
963 }974 }
964975
965 /// Allocates a new register and copies `mcv` into it.976 /// Allocates a new register and copies `mcv` into it.
966 /// `reg_owner` is the instruction that gets associated with the register in the register table.977 /// `reg_owner` is the instruction that gets associated with the register in the register table.
967 /// This can have a side effect of spilling instructions to the stack to free up a register.978 /// This can have a side effect of spilling instructions to the stack to free up a register.
968 fn copyToNewRegister(self: *Self, reg_owner: *ir.Inst, mcv: MCValue) !MCValue {979 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
969 const reg = try self.register_manager.allocReg(reg_owner, &.{});980 const reg = try self.register_manager.allocReg(reg_owner, &.{});
970 try self.genSetReg(reg_owner.src, reg_owner.ty, reg, mcv);981 try self.genSetReg(reg_owner.ty, reg, mcv);
971 return MCValue{ .register = reg };982 return MCValue{ .register = reg };
972 }983 }
973984
974 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {985 fn genAlloc(self: *Self, inst: Air.Inst.Index) !MCValue {
975 const stack_offset = try self.allocMemPtr(&inst.base);986 const stack_offset = try self.allocMemPtr(inst);
976 return MCValue{ .ptr_stack_offset = stack_offset };987 return MCValue{ .ptr_stack_offset = stack_offset };
977 }988 }
978989
979 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {990 fn genFloatCast(self: *Self, inst: Air.Inst.Index) !MCValue {
980 // No side effects, so if it's unreferenced, do nothing.991 // No side effects, so if it's unreferenced, do nothing.
981 if (inst.base.isUnused())992 if (self.liveness.isUnused(inst))
982 return MCValue.dead;993 return MCValue.dead;
983 switch (arch) {994 switch (arch) {
984 else => return self.fail(inst.base.src, "TODO implement floatCast for {}", .{self.target.cpu.arch}),995 else => return self.fail("TODO implement floatCast for {}", .{self.target.cpu.arch}),
985 }996 }
986 }997 }
987998
988 fn genIntCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {999 fn genIntCast(self: *Self, inst: Air.Inst.Index) !MCValue {
989 // No side effects, so if it's unreferenced, do nothing.1000 // No side effects, so if it's unreferenced, do nothing.
990 if (inst.base.isUnused())1001 if (self.liveness.isUnused(inst))
991 return MCValue.dead;1002 return MCValue.dead;
9921003
993 const operand = try self.resolveInst(inst.operand);1004 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
994 const info_a = inst.operand.ty.intInfo(self.target.*);1005 const operand_ty = self.air.getType(ty_op.operand);
995 const info_b = inst.base.ty.intInfo(self.target.*);1006 const operand = try self.resolveInst(ty_op.operand);
1007 const info_a = operand_ty.intInfo(self.target.*);
1008 const info_b = self.air.getType(inst).intInfo(self.target.*);
996 if (info_a.signedness != info_b.signedness)1009 if (info_a.signedness != info_b.signedness)
997 return self.fail(inst.base.src, "TODO gen intcast sign safety in semantic analysis", .{});1010 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
9981011
999 if (info_a.bits == info_b.bits)1012 if (info_a.bits == info_b.bits)
1000 return operand;1013 return operand;
10011014
1002 switch (arch) {1015 switch (arch) {
1003 else => return self.fail(inst.base.src, "TODO implement intCast for {}", .{self.target.cpu.arch}),1016 else => return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}),
1004 }1017 }
1005 }1018 }
10061019
1007 fn genNot(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1020 fn genNot(self: *Self, inst: Air.Inst.Index) !MCValue {
1008 // No side effects, so if it's unreferenced, do nothing.1021 // No side effects, so if it's unreferenced, do nothing.
1009 if (inst.base.isUnused())1022 if (self.liveness.isUnused(inst))
1010 return MCValue.dead;1023 return MCValue.dead;
1011 const operand = try self.resolveInst(inst.operand);1024 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1025 const operand = try self.resolveInst(ty_op.operand);
1012 switch (operand) {1026 switch (operand) {
1013 .dead => unreachable,1027 .dead => unreachable,
1014 .unreach => unreachable,1028 .unreach => unreachable,
...@@ -1037,216 +1051,209 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1037,216 +1051,209 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
10371051
1038 switch (arch) {1052 switch (arch) {
1039 .x86_64 => {1053 .x86_64 => {
1040 var imm = ir.Inst.Constant{1054 return try self.genX8664BinMath(inst, ty_op.operand, .bool_true);
1041 .base = .{
1042 .tag = .constant,
1043 .deaths = 0,
1044 .ty = inst.operand.ty,
1045 .src = inst.operand.src,
1046 },
1047 .val = Value.initTag(.bool_true),
1048 };
1049 return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base);
1050 },1055 },
1051 .arm, .armeb => {1056 .arm, .armeb => {
1052 var imm = ir.Inst.Constant{1057 return try self.genArmBinOp(inst, ty_op.operand, .bool_true, .not);
1053 .base = .{
1054 .tag = .constant,
1055 .deaths = 0,
1056 .ty = inst.operand.ty,
1057 .src = inst.operand.src,
1058 },
1059 .val = Value.initTag(.bool_true),
1060 };
1061 return try self.genArmBinOp(&inst.base, inst.operand, &imm.base, .not);
1062 },1058 },
1063 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),1059 else => return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}),
1064 }1060 }
1065 }1061 }
10661062
1067 fn genAdd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1063 fn genAdd(self: *Self, inst: Air.Inst.Index) !MCValue {
1068 // No side effects, so if it's unreferenced, do nothing.1064 // No side effects, so if it's unreferenced, do nothing.
1069 if (inst.base.isUnused())1065 if (self.liveness.isUnused(inst))
1070 return MCValue.dead;1066 return MCValue.dead;
1067 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1071 switch (arch) {1068 switch (arch) {
1072 .x86_64 => {1069 .x86_64 => {
1073 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs);1070 return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs);
1074 },1071 },
1075 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .add),1072 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .add),
1076 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),1073 else => return self.fail("TODO implement add for {}", .{self.target.cpu.arch}),
1077 }1074 }
1078 }1075 }
10791076
1080 fn genAddWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1077 fn genAddWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1081 // No side effects, so if it's unreferenced, do nothing.1078 // No side effects, so if it's unreferenced, do nothing.
1082 if (inst.base.isUnused())1079 if (self.liveness.isUnused(inst))
1083 return MCValue.dead;1080 return MCValue.dead;
1081 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1082 _ = bin_op;
1084 switch (arch) {1083 switch (arch) {
1085 else => return self.fail(inst.base.src, "TODO implement addwrap for {}", .{self.target.cpu.arch}),1084 else => return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}),
1086 }1085 }
1087 }1086 }
10881087
1089 fn genMul(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1088 fn genMul(self: *Self, inst: Air.Inst.Index) !MCValue {
1090 // No side effects, so if it's unreferenced, do nothing.1089 // No side effects, so if it's unreferenced, do nothing.
1091 if (inst.base.isUnused())1090 if (self.liveness.isUnused(inst))
1092 return MCValue.dead;1091 return MCValue.dead;
1092 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1093 switch (arch) {1093 switch (arch) {
1094 .x86_64 => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),1094 .x86_64 => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1095 .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs),1095 .arm, .armeb => return try self.genArmMul(inst, bin_op.lhs, bin_op.rhs),
1096 else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}),1096 else => return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}),
1097 }1097 }
1098 }1098 }
10991099
1100 fn genMulWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1100 fn genMulWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1101 // No side effects, so if it's unreferenced, do nothing.1101 // No side effects, so if it's unreferenced, do nothing.
1102 if (inst.base.isUnused())1102 if (self.liveness.isUnused(inst))
1103 return MCValue.dead;1103 return MCValue.dead;
1104 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1105 _ = bin_op;
1104 switch (arch) {1106 switch (arch) {
1105 else => return self.fail(inst.base.src, "TODO implement mulwrap for {}", .{self.target.cpu.arch}),1107 else => return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}),
1106 }1108 }
1107 }1109 }
11081110
1109 fn genDiv(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1111 fn genDiv(self: *Self, inst: Air.Inst.Index) !MCValue {
1110 // No side effects, so if it's unreferenced, do nothing.1112 // No side effects, so if it's unreferenced, do nothing.
1111 if (inst.base.isUnused())1113 if (self.liveness.isUnused(inst))
1112 return MCValue.dead;1114 return MCValue.dead;
1115 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1116 _ = bin_op;
1113 switch (arch) {1117 switch (arch) {
1114 else => return self.fail(inst.base.src, "TODO implement div for {}", .{self.target.cpu.arch}),1118 else => return self.fail("TODO implement div for {}", .{self.target.cpu.arch}),
1115 }1119 }
1116 }1120 }
11171121
1118 fn genBitAnd(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1122 fn genBitAnd(self: *Self, inst: Air.Inst.Index) !MCValue {
1119 // No side effects, so if it's unreferenced, do nothing.1123 // No side effects, so if it's unreferenced, do nothing.
1120 if (inst.base.isUnused())1124 if (self.liveness.isUnused(inst))
1121 return MCValue.dead;1125 return MCValue.dead;
1126 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1122 switch (arch) {1127 switch (arch) {
1123 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_and),1128 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_and),
1124 else => return self.fail(inst.base.src, "TODO implement bitwise and for {}", .{self.target.cpu.arch}),1129 else => return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}),
1125 }1130 }
1126 }1131 }
11271132
1128 fn genBitOr(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1133 fn genBitOr(self: *Self, inst: Air.Inst.Index) !MCValue {
1129 // No side effects, so if it's unreferenced, do nothing.1134 // No side effects, so if it's unreferenced, do nothing.
1130 if (inst.base.isUnused())1135 if (self.liveness.isUnused(inst))
1131 return MCValue.dead;1136 return MCValue.dead;
1137 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1132 switch (arch) {1138 switch (arch) {
1133 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bit_or),1139 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bit_or),
1134 else => return self.fail(inst.base.src, "TODO implement bitwise or for {}", .{self.target.cpu.arch}),1140 else => return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}),
1135 }1141 }
1136 }1142 }
11371143
1138 fn genXor(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1144 fn genXor(self: *Self, inst: Air.Inst.Index) !MCValue {
1139 // No side effects, so if it's unreferenced, do nothing.1145 // No side effects, so if it's unreferenced, do nothing.
1140 if (inst.base.isUnused())1146 if (self.liveness.isUnused(inst))
1141 return MCValue.dead;1147 return MCValue.dead;
1148 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1142 switch (arch) {1149 switch (arch) {
1143 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .xor),1150 .arm, .armeb => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .xor),
1144 else => return self.fail(inst.base.src, "TODO implement xor for {}", .{self.target.cpu.arch}),1151 else => return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}),
1145 }1152 }
1146 }1153 }
11471154
1148 fn genOptionalPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1155 fn genOptionalPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1149 // No side effects, so if it's unreferenced, do nothing.1156 // No side effects, so if it's unreferenced, do nothing.
1150 if (inst.base.isUnused())1157 if (self.liveness.isUnused(inst))
1151 return MCValue.dead;1158 return MCValue.dead;
1152 switch (arch) {1159 switch (arch) {
1153 else => return self.fail(inst.base.src, "TODO implement .optional_payload for {}", .{self.target.cpu.arch}),1160 else => return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}),
1154 }1161 }
1155 }1162 }
11561163
1157 fn genOptionalPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1164 fn genOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1158 // No side effects, so if it's unreferenced, do nothing.1165 // No side effects, so if it's unreferenced, do nothing.
1159 if (inst.base.isUnused())1166 if (self.liveness.isUnused(inst))
1160 return MCValue.dead;1167 return MCValue.dead;
1161 switch (arch) {1168 switch (arch) {
1162 else => return self.fail(inst.base.src, "TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),1169 else => return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}),
1163 }1170 }
1164 }1171 }
11651172
1166 fn genUnwrapErrErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1173 fn genUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !MCValue {
1167 // No side effects, so if it's unreferenced, do nothing.1174 // No side effects, so if it's unreferenced, do nothing.
1168 if (inst.base.isUnused())1175 if (self.liveness.isUnused(inst))
1169 return MCValue.dead;1176 return MCValue.dead;
1170 switch (arch) {1177 switch (arch) {
1171 else => return self.fail(inst.base.src, "TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),1178 else => return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
1172 }1179 }
1173 }1180 }
11741181
1175 fn genUnwrapErrPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1182 fn genUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1176 // No side effects, so if it's unreferenced, do nothing.1183 // No side effects, so if it's unreferenced, do nothing.
1177 if (inst.base.isUnused())1184 if (self.liveness.isUnused(inst))
1178 return MCValue.dead;1185 return MCValue.dead;
1179 switch (arch) {1186 switch (arch) {
1180 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),1187 else => return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
1181 }1188 }
1182 }1189 }
1183 // *(E!T) -> E1190 // *(E!T) -> E
1184 fn genUnwrapErrErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1191 fn genUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1185 // No side effects, so if it's unreferenced, do nothing.1192 // No side effects, so if it's unreferenced, do nothing.
1186 if (inst.base.isUnused())1193 if (self.liveness.isUnused(inst))
1187 return MCValue.dead;1194 return MCValue.dead;
1188 switch (arch) {1195 switch (arch) {
1189 else => return self.fail(inst.base.src, "TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),1196 else => return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1190 }1197 }
1191 }1198 }
1192 // *(E!T) -> *T1199 // *(E!T) -> *T
1193 fn genUnwrapErrPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1200 fn genUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1194 // No side effects, so if it's unreferenced, do nothing.1201 // No side effects, so if it's unreferenced, do nothing.
1195 if (inst.base.isUnused())1202 if (self.liveness.isUnused(inst))
1196 return MCValue.dead;1203 return MCValue.dead;
1197 switch (arch) {1204 switch (arch) {
1198 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),1205 else => return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1199 }1206 }
1200 }1207 }
1201 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1208 fn genWrapOptional(self: *Self, inst: Air.Inst.Index) !MCValue {
1202 const optional_ty = inst.base.ty;
1203
1204 // No side effects, so if it's unreferenced, do nothing.1209 // No side effects, so if it's unreferenced, do nothing.
1205 if (inst.base.isUnused())1210 if (self.liveness.isUnused(inst))
1206 return MCValue.dead;1211 return MCValue.dead;
12071212
1213 const optional_ty = self.air.getType(inst);
1214
1208 // Optional type is just a boolean true1215 // Optional type is just a boolean true
1209 if (optional_ty.abiSize(self.target.*) == 1)1216 if (optional_ty.abiSize(self.target.*) == 1)
1210 return MCValue{ .immediate = 1 };1217 return MCValue{ .immediate = 1 };
12111218
1212 switch (arch) {1219 switch (arch) {
1213 else => return self.fail(inst.base.src, "TODO implement wrap optional for {}", .{self.target.cpu.arch}),1220 else => return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}),
1214 }1221 }
1215 }1222 }
12161223
1217 /// T to E!T1224 /// T to E!T
1218 fn genWrapErrUnionPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1225 fn genWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !MCValue {
1219 // No side effects, so if it's unreferenced, do nothing.1226 // No side effects, so if it's unreferenced, do nothing.
1220 if (inst.base.isUnused())1227 if (self.liveness.isUnused(inst))
1221 return MCValue.dead;1228 return MCValue.dead;
12221229
1223 switch (arch) {1230 switch (arch) {
1224 else => return self.fail(inst.base.src, "TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),1231 else => return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1225 }1232 }
1226 }1233 }
12271234
1228 /// E to E!T1235 /// E to E!T
1229 fn genWrapErrUnionErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1236 fn genWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !MCValue {
1230 // No side effects, so if it's unreferenced, do nothing.1237 // No side effects, so if it's unreferenced, do nothing.
1231 if (inst.base.isUnused())1238 if (self.liveness.isUnused(inst))
1232 return MCValue.dead;1239 return MCValue.dead;
12331240
1234 switch (arch) {1241 switch (arch) {
1235 else => return self.fail(inst.base.src, "TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),1242 else => return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1236 }1243 }
1237 }1244 }
1238 fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {1245 fn genVarPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1239 // No side effects, so if it's unreferenced, do nothing.1246 // No side effects, so if it's unreferenced, do nothing.
1240 if (inst.base.isUnused())1247 if (self.liveness.isUnused(inst))
1241 return MCValue.dead;1248 return MCValue.dead;
12421249
1243 switch (arch) {1250 switch (arch) {
1244 else => return self.fail(inst.base.src, "TODO implement varptr for {}", .{self.target.cpu.arch}),1251 else => return self.fail("TODO implement varptr for {}", .{self.target.cpu.arch}),
1245 }1252 }
1246 }1253 }
12471254
1248 fn reuseOperand(self: *Self, inst: *ir.Inst, op_index: ir.Inst.DeathsBitIndex, mcv: MCValue) bool {1255 fn reuseOperand(self: *Self, inst: Air.Inst.Index, op_index: u2, mcv: MCValue) bool {
1249 if (!inst.operandDies(op_index))1256 if (!self.liveness.operandDies(inst, op_index))
1250 return false;1257 return false;
12511258
1252 switch (mcv) {1259 switch (mcv) {
...@@ -1258,16 +1265,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1258,16 +1265,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1258 self.register_manager.registers[index] = inst;1265 self.register_manager.registers[index] = inst;
1259 }1266 }
1260 }1267 }
1261 log.debug("reusing {} => {*}", .{ reg, inst });1268 log.debug("reusing {} => {}", .{ reg, inst });
1262 },1269 },
1263 .stack_offset => |off| {1270 .stack_offset => |off| {
1264 log.debug("reusing stack offset {} => {*}", .{ off, inst });1271 log.debug("reusing stack offset {} => {}", .{ off, inst });
1265 },1272 },
1266 else => return false,1273 else => return false,
1267 }1274 }
12681275
1269 // Prevent the operand deaths processing code from deallocating it.1276 // Prevent the operand deaths processing code from deallocating it.
1270 inst.clearOperandDeath(op_index);1277 self.liveness.clearOperandDeath(inst, op_index);
12711278
1272 // That makes us responsible for doing the rest of the stuff that processDeath would have done.1279 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1273 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];1280 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -1276,22 +1283,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1276,22 +1283,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1276 return true;1283 return true;
1277 }1284 }
12781285
1279 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1286 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue) !void {
1280 const elem_ty = inst.base.ty;
1281 if (!elem_ty.hasCodeGenBits())
1282 return MCValue.none;
1283 const ptr = try self.resolveInst(inst.operand);
1284 const is_volatile = inst.operand.ty.isVolatilePtr();
1285 if (inst.base.isUnused() and !is_volatile)
1286 return MCValue.dead;
1287 const dst_mcv: MCValue = blk: {
1288 if (self.reuseOperand(&inst.base, 0, ptr)) {
1289 // The MCValue that holds the pointer can be re-used as the value.
1290 break :blk ptr;
1291 } else {
1292 break :blk try self.allocRegOrMem(&inst.base, true);
1293 }
1294 };
1295 switch (ptr) {1287 switch (ptr) {
1296 .none => unreachable,1288 .none => unreachable,
1297 .undef => unreachable,1289 .undef => unreachable,
...@@ -1299,31 +1291,51 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1299,31 +1291,51 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1299 .dead => unreachable,1291 .dead => unreachable,
1300 .compare_flags_unsigned => unreachable,1292 .compare_flags_unsigned => unreachable,
1301 .compare_flags_signed => unreachable,1293 .compare_flags_signed => unreachable,
1302 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),1294 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1303 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),1295 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
1304 .ptr_embedded_in_code => |off| {1296 .ptr_embedded_in_code => |off| {
1305 try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });1297 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
1306 },1298 },
1307 .embedded_in_code => {1299 .embedded_in_code => {
1308 return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});1300 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
1309 },1301 },
1310 .register => {1302 .register => {
1311 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});1303 return self.fail("TODO implement loading from MCValue.register", .{});
1312 },1304 },
1313 .memory => {1305 .memory => {
1314 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});1306 return self.fail("TODO implement loading from MCValue.memory", .{});
1315 },1307 },
1316 .stack_offset => {1308 .stack_offset => {
1317 return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});1309 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1318 },1310 },
1319 }1311 }
1312 }
1313
1314 fn genLoad(self: *Self, inst: Air.Inst.Index) !MCValue {
1315 const elem_ty = self.air.getType(inst);
1316 if (!elem_ty.hasCodeGenBits())
1317 return MCValue.none;
1318 const ptr = try self.resolveInst(inst.operand);
1319 const is_volatile = inst.operand.ty.isVolatilePtr();
1320 if (self.liveness.isUnused(inst) and !is_volatile)
1321 return MCValue.dead;
1322 const dst_mcv: MCValue = blk: {
1323 if (self.reuseOperand(inst, 0, ptr)) {
1324 // The MCValue that holds the pointer can be re-used as the value.
1325 break :blk ptr;
1326 } else {
1327 break :blk try self.allocRegOrMem(inst, true);
1328 }
1329 };
1330 self.load(dst_mcv, ptr);
1320 return dst_mcv;1331 return dst_mcv;
1321 }1332 }
13221333
1323 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1334 fn genStore(self: *Self, inst: Air.Inst.Index) !MCValue {
1324 const ptr = try self.resolveInst(inst.lhs);1335 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1325 const value = try self.resolveInst(inst.rhs);1336 const ptr = try self.resolveInst(bin_op.lhs);
1326 const elem_ty = inst.rhs.ty;1337 const value = try self.resolveInst(bin_op.rhs);
1338 const elem_ty = self.getType(bin_op.rhs);
1327 switch (ptr) {1339 switch (ptr) {
1328 .none => unreachable,1340 .none => unreachable,
1329 .undef => unreachable,1341 .undef => unreachable,
...@@ -1332,57 +1344,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1332,57 +1344,60 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1332 .compare_flags_unsigned => unreachable,1344 .compare_flags_unsigned => unreachable,
1333 .compare_flags_signed => unreachable,1345 .compare_flags_signed => unreachable,
1334 .immediate => |imm| {1346 .immediate => |imm| {
1335 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);1347 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1336 },1348 },
1337 .ptr_stack_offset => |off| {1349 .ptr_stack_offset => |off| {
1338 try self.genSetStack(inst.base.src, elem_ty, off, value);1350 try self.genSetStack(elem_ty, off, value);
1339 },1351 },
1340 .ptr_embedded_in_code => |off| {1352 .ptr_embedded_in_code => |off| {
1341 try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);1353 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
1342 },1354 },
1343 .embedded_in_code => {1355 .embedded_in_code => {
1344 return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});1356 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
1345 },1357 },
1346 .register => {1358 .register => {
1347 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});1359 return self.fail("TODO implement storing to MCValue.register", .{});
1348 },1360 },
1349 .memory => {1361 .memory => {
1350 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});1362 return self.fail("TODO implement storing to MCValue.memory", .{});
1351 },1363 },
1352 .stack_offset => {1364 .stack_offset => {
1353 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});1365 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
1354 },1366 },
1355 }1367 }
1356 return .none;1368 return .none;
1357 }1369 }
13581370
1359 fn genStructFieldPtr(self: *Self, inst: *ir.Inst.StructFieldPtr) !MCValue {1371 fn genStructFieldPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
1360 return self.fail(inst.base.src, "TODO implement codegen struct_field_ptr", .{});1372 const struct_field_ptr = self.air.instructions.items(.data)[inst].struct_field_ptr;
1373 _ = struct_field_ptr;
1374 return self.fail("TODO implement codegen struct_field_ptr", .{});
1361 }1375 }
13621376
1363 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1377 fn genSub(self: *Self, inst: Air.Inst.Index) !MCValue {
1364 // No side effects, so if it's unreferenced, do nothing.1378 // No side effects, so if it's unreferenced, do nothing.
1365 if (inst.base.isUnused())1379 if (self.liveness.isUnused(inst))
1366 return MCValue.dead;1380 return MCValue.dead;
1381 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1367 switch (arch) {1382 switch (arch) {
1368 .x86_64 => {1383 .x86_64 => return self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
1369 return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs);1384 .arm, .armeb => return self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .sub),
1370 },1385 else => return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}),
1371 .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .sub),
1372 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
1373 }1386 }
1374 }1387 }
13751388
1376 fn genSubWrap(self: *Self, inst: *ir.Inst.BinOp) !MCValue {1389 fn genSubWrap(self: *Self, inst: Air.Inst.Index) !MCValue {
1377 // No side effects, so if it's unreferenced, do nothing.1390 // No side effects, so if it's unreferenced, do nothing.
1378 if (inst.base.isUnused())1391 if (self.liveness.isUnused(inst))
1379 return MCValue.dead;1392 return MCValue.dead;
1393 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1394 _ = bin_op;
1380 switch (arch) {1395 switch (arch) {
1381 else => return self.fail(inst.base.src, "TODO implement subwrap for {}", .{self.target.cpu.arch}),1396 else => return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}),
1382 }1397 }
1383 }1398 }
13841399
1385 fn armOperandShouldBeRegister(self: *Self, src: LazySrcLoc, mcv: MCValue) !bool {1400 fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1386 return switch (mcv) {1401 return switch (mcv) {
1387 .none => unreachable,1402 .none => unreachable,
1388 .undef => unreachable,1403 .undef => unreachable,
...@@ -1392,7 +1407,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1392,7 +1407,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1392 .ptr_stack_offset => unreachable,1407 .ptr_stack_offset => unreachable,
1393 .ptr_embedded_in_code => unreachable,1408 .ptr_embedded_in_code => unreachable,
1394 .immediate => |imm| blk: {1409 .immediate => |imm| blk: {
1395 if (imm > std.math.maxInt(u32)) return self.fail(src, "TODO ARM binary arithmetic immediate larger than u32", .{});1410 if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{});
13961411
1397 // Load immediate into register if it doesn't fit1412 // Load immediate into register if it doesn't fit
1398 // in an operand1413 // in an operand
...@@ -1406,14 +1421,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1406,14 +1421,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1406 };1421 };
1407 }1422 }
14081423
1409 fn genArmBinOp(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, op: ir.Inst.Tag) !MCValue {1424 fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: ir.Inst.Tag) !MCValue {
1410 const lhs = try self.resolveInst(op_lhs);1425 const lhs = try self.resolveInst(op_lhs);
1411 const rhs = try self.resolveInst(op_rhs);1426 const rhs = try self.resolveInst(op_rhs);
14121427
1413 const lhs_is_register = lhs == .register;1428 const lhs_is_register = lhs == .register;
1414 const rhs_is_register = rhs == .register;1429 const rhs_is_register = rhs == .register;
1415 const lhs_should_be_register = try self.armOperandShouldBeRegister(op_lhs.src, lhs);1430 const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs);
1416 const rhs_should_be_register = try self.armOperandShouldBeRegister(op_rhs.src, rhs);1431 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1417 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);1432 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, 0, lhs);
1418 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);1433 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, 1, rhs);
14191434
...@@ -1486,14 +1501,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1486,14 +1501,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
14861501
1487 // Move the operands to the newly allocated registers1502 // Move the operands to the newly allocated registers
1488 if (lhs_mcv == .register and !lhs_is_register) {1503 if (lhs_mcv == .register and !lhs_is_register) {
1489 try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs);1504 try self.genSetReg(op_lhs.ty, lhs_mcv.register, lhs);
1490 }1505 }
1491 if (rhs_mcv == .register and !rhs_is_register) {1506 if (rhs_mcv == .register and !rhs_is_register) {
1492 try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs);1507 try self.genSetReg(op_rhs.ty, rhs_mcv.register, rhs);
1493 }1508 }
14941509
1495 try self.genArmBinOpCode(1510 try self.genArmBinOpCode(
1496 inst.src,
1497 dst_mcv.register,1511 dst_mcv.register,
1498 lhs_mcv,1512 lhs_mcv,
1499 rhs_mcv,1513 rhs_mcv,
...@@ -1505,14 +1519,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1505,14 +1519,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
15051519
1506 fn genArmBinOpCode(1520 fn genArmBinOpCode(
1507 self: *Self,1521 self: *Self,
1508 src: LazySrcLoc,
1509 dst_reg: Register,1522 dst_reg: Register,
1510 lhs_mcv: MCValue,1523 lhs_mcv: MCValue,
1511 rhs_mcv: MCValue,1524 rhs_mcv: MCValue,
1512 swap_lhs_and_rhs: bool,1525 swap_lhs_and_rhs: bool,
1513 op: ir.Inst.Tag,1526 op: ir.Inst.Tag,
1514 ) !void {1527 ) !void {
1515 _ = src;
1516 assert(lhs_mcv == .register or rhs_mcv == .register);1528 assert(lhs_mcv == .register or rhs_mcv == .register);
15171529
1518 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;1530 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;
...@@ -1561,7 +1573,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1561,7 +1573,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1561 }1573 }
1562 }1574 }
15631575
1564 fn genArmMul(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue {1576 fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Index, op_rhs: Air.Inst.Index) !MCValue {
1565 const lhs = try self.resolveInst(op_lhs);1577 const lhs = try self.resolveInst(op_lhs);
1566 const rhs = try self.resolveInst(op_rhs);1578 const rhs = try self.resolveInst(op_rhs);
15671579
...@@ -1618,10 +1630,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1618,10 +1630,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16181630
1619 // Move the operands to the newly allocated registers1631 // Move the operands to the newly allocated registers
1620 if (!lhs_is_register) {1632 if (!lhs_is_register) {
1621 try self.genSetReg(op_lhs.src, op_lhs.ty, lhs_mcv.register, lhs);1633 try self.genSetReg(op_lhs.ty, lhs_mcv.register, lhs);
1622 }1634 }
1623 if (!rhs_is_register) {1635 if (!rhs_is_register) {
1624 try self.genSetReg(op_rhs.src, op_rhs.ty, rhs_mcv.register, rhs);1636 try self.genSetReg(op_rhs.ty, rhs_mcv.register, rhs);
1625 }1637 }
16261638
1627 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());1639 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());
...@@ -1631,7 +1643,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1631,7 +1643,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1631 /// Perform "binary" operators, excluding comparisons.1643 /// Perform "binary" operators, excluding comparisons.
1632 /// Currently, the following ops are supported:1644 /// Currently, the following ops are supported:
1633 /// ADD, SUB, XOR, OR, AND1645 /// ADD, SUB, XOR, OR, AND
1634 fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue {1646 fn genX8664BinMath(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1635 // We'll handle these ops in two steps.1647 // We'll handle these ops in two steps.
1636 // 1) Prepare an output location (register or memory)1648 // 1) Prepare an output location (register or memory)
1637 // This location will be the location of the operand that dies (if one exists)1649 // This location will be the location of the operand that dies (if one exists)
...@@ -1654,7 +1666,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1654,7 +1666,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1654 // as the result MCValue.1666 // as the result MCValue.
1655 var dst_mcv: MCValue = undefined;1667 var dst_mcv: MCValue = undefined;
1656 var src_mcv: MCValue = undefined;1668 var src_mcv: MCValue = undefined;
1657 var src_inst: *ir.Inst = undefined;1669 var src_inst: Air.Inst.Index = undefined;
1658 if (self.reuseOperand(inst, 0, lhs)) {1670 if (self.reuseOperand(inst, 0, lhs)) {
1659 // LHS dies; use it as the destination.1671 // LHS dies; use it as the destination.
1660 // Both operands cannot be memory.1672 // Both operands cannot be memory.
...@@ -1696,20 +1708,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1696,20 +1708,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1696 switch (src_mcv) {1708 switch (src_mcv) {
1697 .immediate => |imm| {1709 .immediate => |imm| {
1698 if (imm > math.maxInt(u31)) {1710 if (imm > math.maxInt(u31)) {
1699 src_mcv = MCValue{ .register = try self.copyToTmpRegister(src_inst.src, Type.initTag(.u64), src_mcv) };1711 src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.u64), src_mcv) };
1700 }1712 }
1701 },1713 },
1702 else => {},1714 else => {},
1703 }1715 }
17041716
1705 // Now for step 2, we perform the actual op1717 // Now for step 2, we perform the actual op
1706 switch (inst.tag) {1718 const air_tags = self.air.instructions.items(.tag);
1719 switch (air_tags[inst]) {
1707 // TODO: Generate wrapping and non-wrapping versions separately1720 // TODO: Generate wrapping and non-wrapping versions separately
1708 .add, .addwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 0, 0x00),1721 .add, .addwrap => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 0, 0x00),
1709 .bool_or, .bit_or => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 1, 0x08),1722 .bool_or, .bit_or => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 1, 0x08),
1710 .bool_and, .bit_and => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 4, 0x20),1723 .bool_and, .bit_and => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 4, 0x20),
1711 .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28),1724 .sub, .subwrap => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 5, 0x28),
1712 .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30),1725 .xor, .not => try self.genX8664BinMathCode(inst.ty, dst_mcv, src_mcv, 6, 0x30),
17131726
1714 .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv),1727 .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv),
1715 else => unreachable,1728 else => unreachable,
...@@ -1719,16 +1732,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1719,16 +1732,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1719 }1732 }
17201733
1721 /// Wrap over Instruction.encodeInto to translate errors1734 /// Wrap over Instruction.encodeInto to translate errors
1722 fn encodeX8664Instruction(1735 fn encodeX8664Instruction(self: *Self, inst: Instruction) !void {
1723 self: *Self,
1724 src: LazySrcLoc,
1725 inst: Instruction,
1726 ) !void {
1727 inst.encodeInto(self.code) catch |err| {1736 inst.encodeInto(self.code) catch |err| {
1728 if (err == error.OutOfMemory)1737 if (err == error.OutOfMemory)
1729 return error.OutOfMemory1738 return error.OutOfMemory
1730 else1739 else
1731 return self.fail(src, "Instruction.encodeInto failed because {s}", .{@errorName(err)});1740 return self.fail("Instruction.encodeInto failed because {s}", .{@errorName(err)});
1732 };1741 };
1733 }1742 }
17341743
...@@ -1800,7 +1809,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1800,7 +1809,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1800 /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1)1809 /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1)
1801 fn genX8664BinMathCode(1810 fn genX8664BinMathCode(
1802 self: *Self,1811 self: *Self,
1803 src: LazySrcLoc,
1804 dst_ty: Type,1812 dst_ty: Type,
1805 dst_mcv: MCValue,1813 dst_mcv: MCValue,
1806 src_mcv: MCValue,1814 src_mcv: MCValue,
...@@ -1818,7 +1826,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1818,7 +1826,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1818 .register => |dst_reg| {1826 .register => |dst_reg| {
1819 switch (src_mcv) {1827 switch (src_mcv) {
1820 .none => unreachable,1828 .none => unreachable,
1821 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),1829 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
1822 .dead, .unreach => unreachable,1830 .dead, .unreach => unreachable,
1823 .ptr_stack_offset => unreachable,1831 .ptr_stack_offset => unreachable,
1824 .ptr_embedded_in_code => unreachable,1832 .ptr_embedded_in_code => unreachable,
...@@ -1872,7 +1880,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1872,7 +1880,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1872 }1880 }
1873 },1881 },
1874 .embedded_in_code, .memory => {1882 .embedded_in_code, .memory => {
1875 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});1883 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
1876 },1884 },
1877 .stack_offset => |off| {1885 .stack_offset => |off| {
1878 // register, indirect use mr + 31886 // register, indirect use mr + 3
...@@ -1880,7 +1888,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1880,7 +1888,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1880 const abi_size = dst_ty.abiSize(self.target.*);1888 const abi_size = dst_ty.abiSize(self.target.*);
1881 const adj_off = off + abi_size;1889 const adj_off = off + abi_size;
1882 if (off > math.maxInt(i32)) {1890 if (off > math.maxInt(i32)) {
1883 return self.fail(src, "stack offset too large", .{});1891 return self.fail("stack offset too large", .{});
1884 }1892 }
1885 const encoder = try X8664Encoder.init(self.code, 7);1893 const encoder = try X8664Encoder.init(self.code, 7);
1886 encoder.rex(.{1894 encoder.rex(.{
...@@ -1903,17 +1911,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1903,17 +1911,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1903 }1911 }
1904 },1912 },
1905 .compare_flags_unsigned => {1913 .compare_flags_unsigned => {
1906 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});1914 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
1907 },1915 },
1908 .compare_flags_signed => {1916 .compare_flags_signed => {
1909 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});1917 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
1910 },1918 },
1911 }1919 }
1912 },1920 },
1913 .stack_offset => |off| {1921 .stack_offset => |off| {
1914 switch (src_mcv) {1922 switch (src_mcv) {
1915 .none => unreachable,1923 .none => unreachable,
1916 .undef => return self.genSetStack(src, dst_ty, off, .undef),1924 .undef => return self.genSetStack(dst_ty, off, .undef),
1917 .dead, .unreach => unreachable,1925 .dead, .unreach => unreachable,
1918 .ptr_stack_offset => unreachable,1926 .ptr_stack_offset => unreachable,
1919 .ptr_embedded_in_code => unreachable,1927 .ptr_embedded_in_code => unreachable,
...@@ -1922,21 +1930,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1922,21 +1930,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1922 },1930 },
1923 .immediate => |imm| {1931 .immediate => |imm| {
1924 _ = imm;1932 _ = imm;
1925 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source immediate", .{});1933 return self.fail("TODO implement x86 ADD/SUB/CMP source immediate", .{});
1926 },1934 },
1927 .embedded_in_code, .memory, .stack_offset => {1935 .embedded_in_code, .memory, .stack_offset => {
1928 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});1936 return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{});
1929 },1937 },
1930 .compare_flags_unsigned => {1938 .compare_flags_unsigned => {
1931 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});1939 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
1932 },1940 },
1933 .compare_flags_signed => {1941 .compare_flags_signed => {
1934 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});1942 return self.fail("TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
1935 },1943 },
1936 }1944 }
1937 },1945 },
1938 .embedded_in_code, .memory => {1946 .embedded_in_code, .memory => {
1939 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});1947 return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{});
1940 },1948 },
1941 }1949 }
1942 }1950 }
...@@ -1960,7 +1968,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1960,7 +1968,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1960 .register => |dst_reg| {1968 .register => |dst_reg| {
1961 switch (src_mcv) {1969 switch (src_mcv) {
1962 .none => unreachable,1970 .none => unreachable,
1963 .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef),1971 .undef => try self.genSetReg(dst_ty, dst_reg, .undef),
1964 .dead, .unreach => unreachable,1972 .dead, .unreach => unreachable,
1965 .ptr_stack_offset => unreachable,1973 .ptr_stack_offset => unreachable,
1966 .ptr_embedded_in_code => unreachable,1974 .ptr_embedded_in_code => unreachable,
...@@ -2026,31 +2034,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2026,31 +2034,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2026 );2034 );
2027 encoder.imm32(@intCast(i32, imm));2035 encoder.imm32(@intCast(i32, imm));
2028 } else {2036 } else {
2029 const src_reg = try self.copyToTmpRegister(src, dst_ty, src_mcv);2037 const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv);
2030 return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg });2038 return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg });
2031 }2039 }
2032 },2040 },
2033 .embedded_in_code, .memory, .stack_offset => {2041 .embedded_in_code, .memory, .stack_offset => {
2034 return self.fail(src, "TODO implement x86 multiply source memory", .{});2042 return self.fail("TODO implement x86 multiply source memory", .{});
2035 },2043 },
2036 .compare_flags_unsigned => {2044 .compare_flags_unsigned => {
2037 return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{});2045 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
2038 },2046 },
2039 .compare_flags_signed => {2047 .compare_flags_signed => {
2040 return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{});2048 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
2041 },2049 },
2042 }2050 }
2043 },2051 },
2044 .stack_offset => |off| {2052 .stack_offset => |off| {
2045 switch (src_mcv) {2053 switch (src_mcv) {
2046 .none => unreachable,2054 .none => unreachable,
2047 .undef => return self.genSetStack(src, dst_ty, off, .undef),2055 .undef => return self.genSetStack(dst_ty, off, .undef),
2048 .dead, .unreach => unreachable,2056 .dead, .unreach => unreachable,
2049 .ptr_stack_offset => unreachable,2057 .ptr_stack_offset => unreachable,
2050 .ptr_embedded_in_code => unreachable,2058 .ptr_embedded_in_code => unreachable,
2051 .register => |src_reg| {2059 .register => |src_reg| {
2052 // copy dst to a register2060 // copy dst to a register
2053 const dst_reg = try self.copyToTmpRegister(src, dst_ty, dst_mcv);2061 const dst_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
2054 // multiply into dst_reg2062 // multiply into dst_reg
2055 // register, register2063 // register, register
2056 // Use the following imul opcode2064 // Use the following imul opcode
...@@ -2068,34 +2076,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2068,34 +2076,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2068 src_reg.low_id(),2076 src_reg.low_id(),
2069 );2077 );
2070 // copy dst_reg back out2078 // copy dst_reg back out
2071 return self.genSetStack(src, dst_ty, off, MCValue{ .register = dst_reg });2079 return self.genSetStack(dst_ty, off, MCValue{ .register = dst_reg });
2072 },2080 },
2073 .immediate => |imm| {2081 .immediate => |imm| {
2074 _ = imm;2082 _ = imm;
2075 return self.fail(src, "TODO implement x86 multiply source immediate", .{});2083 return self.fail("TODO implement x86 multiply source immediate", .{});
2076 },2084 },
2077 .embedded_in_code, .memory, .stack_offset => {2085 .embedded_in_code, .memory, .stack_offset => {
2078 return self.fail(src, "TODO implement x86 multiply source memory", .{});2086 return self.fail("TODO implement x86 multiply source memory", .{});
2079 },2087 },
2080 .compare_flags_unsigned => {2088 .compare_flags_unsigned => {
2081 return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{});2089 return self.fail("TODO implement x86 multiply source compare flag (unsigned)", .{});
2082 },2090 },
2083 .compare_flags_signed => {2091 .compare_flags_signed => {
2084 return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{});2092 return self.fail("TODO implement x86 multiply source compare flag (signed)", .{});
2085 },2093 },
2086 }2094 }
2087 },2095 },
2088 .embedded_in_code, .memory => {2096 .embedded_in_code, .memory => {
2089 return self.fail(src, "TODO implement x86 multiply destination memory", .{});2097 return self.fail("TODO implement x86 multiply destination memory", .{});
2090 },2098 },
2091 }2099 }
2092 }2100 }
20932101
2094 fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void {2102 fn genX8664ModRMRegToStack(self: *Self, ty: Type, off: u32, reg: Register, opcode: u8) !void {
2095 const abi_size = ty.abiSize(self.target.*);2103 const abi_size = ty.abiSize(self.target.*);
2096 const adj_off = off + abi_size;2104 const adj_off = off + abi_size;
2097 if (off > math.maxInt(i32)) {2105 if (off > math.maxInt(i32)) {
2098 return self.fail(src, "stack offset too large", .{});2106 return self.fail("stack offset too large", .{});
2099 }2107 }
21002108
2101 const i_adj_off = -@intCast(i32, adj_off);2109 const i_adj_off = -@intCast(i32, adj_off);
...@@ -2122,8 +2130,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2122,8 +2130,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2122 }2130 }
2123 }2131 }
21242132
2125 fn genArgDbgInfo(self: *Self, inst: *ir.Inst.Arg, mcv: MCValue) !void {2133 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
2126 const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1];2134 const name_with_null = inst.name[0 .. mem.lenZ(inst.name) + 1];
2135 const ty = self.air.getType(inst);
21272136
2128 switch (mcv) {2137 switch (mcv) {
2129 .register => |reg| {2138 .register => |reg| {
...@@ -2136,7 +2145,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2136,7 +2145,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2136 reg.dwarfLocOp(),2145 reg.dwarfLocOp(),
2137 });2146 });
2138 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);2147 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
2139 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref42148 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
2140 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string2149 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
2141 },2150 },
2142 .none => {},2151 .none => {},
...@@ -2147,12 +2156,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2147,12 +2156,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2147 .dwarf => |dbg_out| {2156 .dwarf => |dbg_out| {
2148 switch (arch) {2157 switch (arch) {
2149 .arm, .armeb => {2158 .arm, .armeb => {
2150 const ty = inst.base.ty;
2151 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {2159 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
2152 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{ty});2160 return self.fail("type '{}' too big to fit into stack frame", .{ty});
2153 };2161 };
2154 const adjusted_stack_offset = math.negateCast(offset + abi_size) catch {2162 const adjusted_stack_offset = math.negateCast(offset + abi_size) catch {
2155 return self.fail(inst.base.src, "Stack offset too large for arguments", .{});2163 return self.fail("Stack offset too large for arguments", .{});
2156 };2164 };
21572165
2158 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);2166 try dbg_out.dbg_info.append(link.File.Elf.abbrev_parameter);
...@@ -2168,7 +2176,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2168,7 +2176,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2168 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);2176 try leb128.writeILEB128(dbg_out.dbg_info.writer(), adjusted_stack_offset);
21692177
2170 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);2178 try dbg_out.dbg_info.ensureCapacity(dbg_out.dbg_info.items.len + 5 + name_with_null.len);
2171 try self.addDbgInfoTypeReloc(inst.base.ty); // DW.AT_type, DW.FORM_ref42179 try self.addDbgInfoTypeReloc(ty); // DW.AT_type, DW.FORM_ref4
2172 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string2180 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT_name, DW.FORM_string
2173 },2181 },
2174 else => {},2182 else => {},
...@@ -2181,23 +2189,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2181,23 +2189,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2181 }2189 }
2182 }2190 }
21832191
2184 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {2192 fn genArg(self: *Self, inst: Air.Inst.Index) !MCValue {
2185 const arg_index = self.arg_index;2193 const arg_index = self.arg_index;
2186 self.arg_index += 1;2194 self.arg_index += 1;
21872195
2196 const ty = self.air.getType(inst);
2197
2188 const result = self.args[arg_index];2198 const result = self.args[arg_index];
2189 const mcv = switch (arch) {2199 const mcv = switch (arch) {
2190 // TODO support stack-only arguments on all target architectures2200 // TODO support stack-only arguments on all target architectures
2191 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {2201 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {
2192 // Copy registers to the stack2202 // Copy registers to the stack
2193 .register => |reg| blk: {2203 .register => |reg| blk: {
2194 const ty = inst.base.ty;
2195 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {2204 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
2196 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{ty});2205 return self.fail("type '{}' too big to fit into stack frame", .{ty});
2197 };2206 };
2198 const abi_align = ty.abiAlignment(self.target.*);2207 const abi_align = ty.abiAlignment(self.target.*);
2199 const stack_offset = try self.allocMem(&inst.base, abi_size, abi_align);2208 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
2200 try self.genSetStack(inst.base.src, ty, stack_offset, MCValue{ .register = reg });2209 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
22012210
2202 break :blk MCValue{ .stack_offset = stack_offset };2211 break :blk MCValue{ .stack_offset = stack_offset };
2203 },2212 },
...@@ -2207,12 +2216,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2207,12 +2216,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2207 };2216 };
2208 try self.genArgDbgInfo(inst, mcv);2217 try self.genArgDbgInfo(inst, mcv);
22092218
2210 if (inst.base.isUnused())2219 if (self.liveness.isUnused(inst))
2211 return MCValue.dead;2220 return MCValue.dead;
22122221
2213 switch (mcv) {2222 switch (mcv) {
2214 .register => |reg| {2223 .register => |reg| {
2215 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), &inst.base);2224 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst);
2216 },2225 },
2217 else => {},2226 else => {},
2218 }2227 }
...@@ -2220,7 +2229,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2220,7 +2229,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2220 return mcv;2229 return mcv;
2221 }2230 }
22222231
2223 fn genBreakpoint(self: *Self, src: LazySrcLoc) !MCValue {2232 fn genBreakpoint(self: *Self) !MCValue {
2224 switch (arch) {2233 switch (arch) {
2225 .i386, .x86_64 => {2234 .i386, .x86_64 => {
2226 try self.code.append(0xcc); // int32235 try self.code.append(0xcc); // int3
...@@ -2234,13 +2243,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2234,13 +2243,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2234 .aarch64 => {2243 .aarch64 => {
2235 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());2244 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
2236 },2245 },
2237 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),2246 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
2238 }2247 }
2239 return .none;2248 return .none;
2240 }2249 }
22412250
2242 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {2251 fn genCall(self: *Self, inst: Air.Inst.Index) !MCValue {
2243 var info = try self.resolveCallingConventionValues(inst.base.src, inst.func.ty);2252 const inst_datas = self.air.instructions.items(.data);
2253 const pl_op = inst_datas[inst].pl_op;
2254 const fn_ty = self.air.getType(pl_op.operand);
2255 const callee = pl_op.operand;
2256 const extra = self.air.extraData(Air.Call, inst_data.payload);
2257 const args = self.air.extra[extra.end..][0..extra.data.args_len];
2258
2259 var info = try self.resolveCallingConventionValues(fn_ty);
2244 defer info.deinit(self);2260 defer info.deinit(self);
22452261
2246 // Due to incremental compilation, how function calls are generated depends2262 // Due to incremental compilation, how function calls are generated depends
...@@ -2249,26 +2265,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2249,26 +2265,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2249 switch (arch) {2265 switch (arch) {
2250 .x86_64 => {2266 .x86_64 => {
2251 for (info.args) |mc_arg, arg_i| {2267 for (info.args) |mc_arg, arg_i| {
2252 const arg = inst.args[arg_i];2268 const arg = args[arg_i];
2253 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2269 const arg_mcv = try self.resolveInst(args[arg_i]);
2254 // Here we do not use setRegOrMem even though the logic is similar, because2270 // Here we do not use setRegOrMem even though the logic is similar, because
2255 // the function call will move the stack pointer, so the offsets are different.2271 // the function call will move the stack pointer, so the offsets are different.
2256 switch (mc_arg) {2272 switch (mc_arg) {
2257 .none => continue,2273 .none => continue,
2258 .register => |reg| {2274 .register => |reg| {
2259 try self.register_manager.getReg(reg, null);2275 try self.register_manager.getReg(reg, null);
2260 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2276 try self.genSetReg(arg.ty, reg, arg_mcv);
2261 },2277 },
2262 .stack_offset => |off| {2278 .stack_offset => |off| {
2263 // Here we need to emit instructions like this:2279 // Here we need to emit instructions like this:
2264 // mov qword ptr [rsp + stack_offset], x2280 // mov qword ptr [rsp + stack_offset], x
2265 try self.genSetStack(arg.src, arg.ty, off, arg_mcv);2281 try self.genSetStack(arg.ty, off, arg_mcv);
2266 },2282 },
2267 .ptr_stack_offset => {2283 .ptr_stack_offset => {
2268 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2284 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2269 },2285 },
2270 .ptr_embedded_in_code => {2286 .ptr_embedded_in_code => {
2271 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2287 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2272 },2288 },
2273 .undef => unreachable,2289 .undef => unreachable,
2274 .immediate => unreachable,2290 .immediate => unreachable,
...@@ -2281,7 +2297,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2281,7 +2297,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2281 }2297 }
2282 }2298 }
22832299
2284 if (inst.func.value()) |func_value| {2300 if (self.air.value(callee)) |func_value| {
2285 if (func_value.castTag(.function)) |func_payload| {2301 if (func_value.castTag(.function)) |func_payload| {
2286 const func = func_payload.data;2302 const func = func_payload.data;
22872303
...@@ -2300,18 +2316,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2300,18 +2316,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2300 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });2316 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2301 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);2317 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
2302 } else if (func_value.castTag(.extern_fn)) |_| {2318 } else if (func_value.castTag(.extern_fn)) |_| {
2303 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2319 return self.fail("TODO implement calling extern functions", .{});
2304 } else {2320 } else {
2305 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2321 return self.fail("TODO implement calling bitcasted functions", .{});
2306 }2322 }
2307 } else {2323 } else {
2308 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2324 return self.fail("TODO implement calling runtime known function pointer", .{});
2309 }2325 }
2310 },2326 },
2311 .riscv64 => {2327 .riscv64 => {
2312 if (info.args.len > 0) return self.fail(inst.base.src, "TODO implement fn args for {}", .{self.target.cpu.arch});2328 if (info.args.len > 0) return self.fail("TODO implement fn args for {}", .{self.target.cpu.arch});
23132329
2314 if (inst.func.value()) |func_value| {2330 if (self.air.value(callee)) |func_value| {
2315 if (func_value.castTag(.function)) |func_payload| {2331 if (func_value.castTag(.function)) |func_payload| {
2316 const func = func_payload.data;2332 const func = func_payload.data;
23172333
...@@ -2325,21 +2341,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2325,21 +2341,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2325 else2341 else
2326 unreachable;2342 unreachable;
23272343
2328 try self.genSetReg(inst.base.src, Type.initTag(.usize), .ra, .{ .memory = got_addr });2344 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
2329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());2345 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
2330 } else if (func_value.castTag(.extern_fn)) |_| {2346 } else if (func_value.castTag(.extern_fn)) |_| {
2331 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2347 return self.fail("TODO implement calling extern functions", .{});
2332 } else {2348 } else {
2333 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2349 return self.fail("TODO implement calling bitcasted functions", .{});
2334 }2350 }
2335 } else {2351 } else {
2336 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2352 return self.fail("TODO implement calling runtime known function pointer", .{});
2337 }2353 }
2338 },2354 },
2339 .arm, .armeb => {2355 .arm, .armeb => {
2340 for (info.args) |mc_arg, arg_i| {2356 for (info.args) |mc_arg, arg_i| {
2341 const arg = inst.args[arg_i];2357 const arg = args[arg_i];
2342 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2358 const arg_mcv = try self.resolveInst(args[arg_i]);
23432359
2344 switch (mc_arg) {2360 switch (mc_arg) {
2345 .none => continue,2361 .none => continue,
...@@ -2353,21 +2369,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2353,21 +2369,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2353 .compare_flags_unsigned => unreachable,2369 .compare_flags_unsigned => unreachable,
2354 .register => |reg| {2370 .register => |reg| {
2355 try self.register_manager.getReg(reg, null);2371 try self.register_manager.getReg(reg, null);
2356 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2372 try self.genSetReg(arg.ty, reg, arg_mcv);
2357 },2373 },
2358 .stack_offset => {2374 .stack_offset => {
2359 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2375 return self.fail("TODO implement calling with parameters in memory", .{});
2360 },2376 },
2361 .ptr_stack_offset => {2377 .ptr_stack_offset => {
2362 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2378 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2363 },2379 },
2364 .ptr_embedded_in_code => {2380 .ptr_embedded_in_code => {
2365 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2381 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2366 },2382 },
2367 }2383 }
2368 }2384 }
23692385
2370 if (inst.func.value()) |func_value| {2386 if (self.air.value(callee)) |func_value| {
2371 if (func_value.castTag(.function)) |func_payload| {2387 if (func_value.castTag(.function)) |func_payload| {
2372 const func = func_payload.data;2388 const func = func_payload.data;
2373 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2389 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -2380,7 +2396,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2380,7 +2396,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2380 else2396 else
2381 unreachable;2397 unreachable;
23822398
2383 try self.genSetReg(inst.base.src, Type.initTag(.usize), .lr, .{ .memory = got_addr });2399 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
23842400
2385 // TODO: add Instruction.supportedOn2401 // TODO: add Instruction.supportedOn
2386 // function for ARM2402 // function for ARM
...@@ -2391,18 +2407,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2391,18 +2407,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2391 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());2407 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32());
2392 }2408 }
2393 } else if (func_value.castTag(.extern_fn)) |_| {2409 } else if (func_value.castTag(.extern_fn)) |_| {
2394 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2410 return self.fail("TODO implement calling extern functions", .{});
2395 } else {2411 } else {
2396 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2412 return self.fail("TODO implement calling bitcasted functions", .{});
2397 }2413 }
2398 } else {2414 } else {
2399 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2415 return self.fail("TODO implement calling runtime known function pointer", .{});
2400 }2416 }
2401 },2417 },
2402 .aarch64 => {2418 .aarch64 => {
2403 for (info.args) |mc_arg, arg_i| {2419 for (info.args) |mc_arg, arg_i| {
2404 const arg = inst.args[arg_i];2420 const arg = args[arg_i];
2405 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2421 const arg_mcv = try self.resolveInst(args[arg_i]);
24062422
2407 switch (mc_arg) {2423 switch (mc_arg) {
2408 .none => continue,2424 .none => continue,
...@@ -2416,21 +2432,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2416,21 +2432,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2416 .compare_flags_unsigned => unreachable,2432 .compare_flags_unsigned => unreachable,
2417 .register => |reg| {2433 .register => |reg| {
2418 try self.register_manager.getReg(reg, null);2434 try self.register_manager.getReg(reg, null);
2419 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2435 try self.genSetReg(arg.ty, reg, arg_mcv);
2420 },2436 },
2421 .stack_offset => {2437 .stack_offset => {
2422 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2438 return self.fail("TODO implement calling with parameters in memory", .{});
2423 },2439 },
2424 .ptr_stack_offset => {2440 .ptr_stack_offset => {
2425 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2441 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2426 },2442 },
2427 .ptr_embedded_in_code => {2443 .ptr_embedded_in_code => {
2428 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2444 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2429 },2445 },
2430 }2446 }
2431 }2447 }
24322448
2433 if (inst.func.value()) |func_value| {2449 if (self.air.value(callee)) |func_value| {
2434 if (func_value.castTag(.function)) |func_payload| {2450 if (func_value.castTag(.function)) |func_payload| {
2435 const func = func_payload.data;2451 const func = func_payload.data;
2436 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2452 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
...@@ -2443,24 +2459,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2443,24 +2459,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2443 else2459 else
2444 unreachable;2460 unreachable;
24452461
2446 try self.genSetReg(inst.base.src, Type.initTag(.usize), .x30, .{ .memory = got_addr });2462 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
24472463
2448 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2464 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2449 } else if (func_value.castTag(.extern_fn)) |_| {2465 } else if (func_value.castTag(.extern_fn)) |_| {
2450 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2466 return self.fail("TODO implement calling extern functions", .{});
2451 } else {2467 } else {
2452 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2468 return self.fail("TODO implement calling bitcasted functions", .{});
2453 }2469 }
2454 } else {2470 } else {
2455 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2471 return self.fail("TODO implement calling runtime known function pointer", .{});
2456 }2472 }
2457 },2473 },
2458 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),2474 else => return self.fail("TODO implement call for {}", .{self.target.cpu.arch}),
2459 }2475 }
2460 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {2476 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2461 for (info.args) |mc_arg, arg_i| {2477 for (info.args) |mc_arg, arg_i| {
2462 const arg = inst.args[arg_i];2478 const arg = args[arg_i];
2463 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2479 const arg_mcv = try self.resolveInst(args[arg_i]);
2464 // Here we do not use setRegOrMem even though the logic is similar, because2480 // Here we do not use setRegOrMem even though the logic is similar, because
2465 // the function call will move the stack pointer, so the offsets are different.2481 // the function call will move the stack pointer, so the offsets are different.
2466 switch (mc_arg) {2482 switch (mc_arg) {
...@@ -2471,18 +2487,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2471,18 +2487,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2471 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),2487 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
2472 else => unreachable,2488 else => unreachable,
2473 }2489 }
2474 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2490 try self.genSetReg(arg.ty, reg, arg_mcv);
2475 },2491 },
2476 .stack_offset => {2492 .stack_offset => {
2477 // Here we need to emit instructions like this:2493 // Here we need to emit instructions like this:
2478 // mov qword ptr [rsp + stack_offset], x2494 // mov qword ptr [rsp + stack_offset], x
2479 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2495 return self.fail("TODO implement calling with parameters in memory", .{});
2480 },2496 },
2481 .ptr_stack_offset => {2497 .ptr_stack_offset => {
2482 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2498 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2483 },2499 },
2484 .ptr_embedded_in_code => {2500 .ptr_embedded_in_code => {
2485 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2501 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2486 },2502 },
2487 .undef => unreachable,2503 .undef => unreachable,
2488 .immediate => unreachable,2504 .immediate => unreachable,
...@@ -2495,7 +2511,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2495,7 +2511,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2495 }2511 }
2496 }2512 }
24972513
2498 if (inst.func.value()) |func_value| {2514 if (self.air.value(callee)) |func_value| {
2499 if (func_value.castTag(.function)) |func_payload| {2515 if (func_value.castTag(.function)) |func_payload| {
2500 const func = func_payload.data;2516 const func = func_payload.data;
2501 const got_addr = blk: {2517 const got_addr = blk: {
...@@ -2506,13 +2522,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2506,13 +2522,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2506 log.debug("got_addr = 0x{x}", .{got_addr});2522 log.debug("got_addr = 0x{x}", .{got_addr});
2507 switch (arch) {2523 switch (arch) {
2508 .x86_64 => {2524 .x86_64 => {
2509 try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .memory = got_addr });2525 try self.genSetReg(Type.initTag(.u64), .rax, .{ .memory = got_addr });
2510 // callq *%rax2526 // callq *%rax
2511 try self.code.ensureCapacity(self.code.items.len + 2);2527 try self.code.ensureCapacity(self.code.items.len + 2);
2512 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });2528 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
2513 },2529 },
2514 .aarch64 => {2530 .aarch64 => {
2515 try self.genSetReg(inst.base.src, Type.initTag(.u64), .x30, .{ .memory = got_addr });2531 try self.genSetReg(Type.initTag(.u64), .x30, .{ .memory = got_addr });
2516 // blr x302532 // blr x30
2517 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2533 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2518 },2534 },
...@@ -2552,35 +2568,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2552,35 +2568,35 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2552 });2568 });
2553 // We mark the space and fix it up later.2569 // We mark the space and fix it up later.
2554 } else {2570 } else {
2555 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2571 return self.fail("TODO implement calling bitcasted functions", .{});
2556 }2572 }
2557 } else {2573 } else {
2558 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2574 return self.fail("TODO implement calling runtime known function pointer", .{});
2559 }2575 }
2560 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {2576 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2561 switch (arch) {2577 switch (arch) {
2562 .x86_64 => {2578 .x86_64 => {
2563 for (info.args) |mc_arg, arg_i| {2579 for (info.args) |mc_arg, arg_i| {
2564 const arg = inst.args[arg_i];2580 const arg = args[arg_i];
2565 const arg_mcv = try self.resolveInst(inst.args[arg_i]);2581 const arg_mcv = try self.resolveInst(args[arg_i]);
2566 // Here we do not use setRegOrMem even though the logic is similar, because2582 // Here we do not use setRegOrMem even though the logic is similar, because
2567 // the function call will move the stack pointer, so the offsets are different.2583 // the function call will move the stack pointer, so the offsets are different.
2568 switch (mc_arg) {2584 switch (mc_arg) {
2569 .none => continue,2585 .none => continue,
2570 .register => |reg| {2586 .register => |reg| {
2571 try self.register_manager.getReg(reg, null);2587 try self.register_manager.getReg(reg, null);
2572 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2588 try self.genSetReg(arg.ty, reg, arg_mcv);
2573 },2589 },
2574 .stack_offset => {2590 .stack_offset => {
2575 // Here we need to emit instructions like this:2591 // Here we need to emit instructions like this:
2576 // mov qword ptr [rsp + stack_offset], x2592 // mov qword ptr [rsp + stack_offset], x
2577 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2593 return self.fail("TODO implement calling with parameters in memory", .{});
2578 },2594 },
2579 .ptr_stack_offset => {2595 .ptr_stack_offset => {
2580 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2596 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2581 },2597 },
2582 .ptr_embedded_in_code => {2598 .ptr_embedded_in_code => {
2583 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2599 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2584 },2600 },
2585 .undef => unreachable,2601 .undef => unreachable,
2586 .immediate => unreachable,2602 .immediate => unreachable,
...@@ -2592,7 +2608,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2592,7 +2608,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2592 .compare_flags_unsigned => unreachable,2608 .compare_flags_unsigned => unreachable,
2593 }2609 }
2594 }2610 }
2595 if (inst.func.value()) |func_value| {2611 if (self.air.value(callee)) |func_value| {
2596 if (func_value.castTag(.function)) |func_payload| {2612 if (func_value.castTag(.function)) |func_payload| {
2597 const ptr_bits = self.target.cpu.arch.ptrBitWidth();2613 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2598 const ptr_bytes: u64 = @divExact(ptr_bits, 8);2614 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
...@@ -2603,9 +2619,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2603,9 +2619,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2603 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });2619 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
2604 const fn_got_addr = got_addr + got_index * ptr_bytes;2620 const fn_got_addr = got_addr + got_index * ptr_bytes;
2605 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));2621 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, fn_got_addr));
2606 } else return self.fail(inst.base.src, "TODO implement calling extern fn on plan9", .{});2622 } else return self.fail("TODO implement calling extern fn on plan9", .{});
2607 } else {2623 } else {
2608 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2624 return self.fail("TODO implement calling runtime known function pointer", .{});
2609 }2625 }
2610 },2626 },
2611 .aarch64 => {2627 .aarch64 => {
...@@ -2628,13 +2644,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2628,13 +2644,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2628 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);2644 try self.genSetReg(arg.src, arg.ty, reg, arg_mcv);
2629 },2645 },
2630 .stack_offset => {2646 .stack_offset => {
2631 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});2647 return self.fail("TODO implement calling with parameters in memory", .{});
2632 },2648 },
2633 .ptr_stack_offset => {2649 .ptr_stack_offset => {
2634 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});2650 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2635 },2651 },
2636 .ptr_embedded_in_code => {2652 .ptr_embedded_in_code => {
2637 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});2653 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2638 },2654 },
2639 }2655 }
2640 }2656 }
...@@ -2650,15 +2666,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2650,15 +2666,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26502666
2651 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());2667 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2652 } else if (func_value.castTag(.extern_fn)) |_| {2668 } else if (func_value.castTag(.extern_fn)) |_| {
2653 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});2669 return self.fail("TODO implement calling extern functions", .{});
2654 } else {2670 } else {
2655 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});2671 return self.fail("TODO implement calling bitcasted functions", .{});
2656 }2672 }
2657 } else {2673 } else {
2658 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});2674 return self.fail("TODO implement calling runtime known function pointer", .{});
2659 }2675 }
2660 },2676 },
2661 else => return self.fail(inst.base.src, "TODO implement call on plan9 for {}", .{self.target.cpu.arch}),2677 else => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
2662 }2678 }
2663 } else unreachable;2679 } else unreachable;
26642680
...@@ -2666,7 +2682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2666,7 +2682,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2666 .register => |reg| {2682 .register => |reg| {
2667 if (Register.allocIndex(reg) == null) {2683 if (Register.allocIndex(reg) == null) {
2668 // Save function return value in a callee saved register2684 // Save function return value in a callee saved register
2669 return try self.copyToNewRegister(&inst.base, info.return_value);2685 return try self.copyToNewRegister(inst, info.return_value);
2670 }2686 }
2671 },2687 },
2672 else => {},2688 else => {},
...@@ -2675,8 +2691,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2675,8 +2691,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2675 return info.return_value;2691 return info.return_value;
2676 }2692 }
26772693
2678 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2694 fn genRef(self: *Self, inst: Air.Inst.Index) !MCValue {
2679 const operand = try self.resolveInst(inst.operand);2695 if (self.liveness.isUnused(inst))
2696 return MCValue.dead;
2697 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2698 const operand_ty = self.air.getType(ty_op.operand);
2699 const operand = try self.resolveInst(ty_op.operand);
2680 switch (operand) {2700 switch (operand) {
2681 .unreach => unreachable,2701 .unreach => unreachable,
2682 .dead => unreachable,2702 .dead => unreachable,
...@@ -2689,8 +2709,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2689,8 +2709,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2689 .compare_flags_unsigned,2709 .compare_flags_unsigned,
2690 .compare_flags_signed,2710 .compare_flags_signed,
2691 => {2711 => {
2692 const stack_offset = try self.allocMemPtr(&inst.base);2712 const stack_offset = try self.allocMemPtr(inst);
2693 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);2713 try self.genSetStack(operand_ty, stack_offset, operand);
2694 return MCValue{ .ptr_stack_offset = stack_offset };2714 return MCValue{ .ptr_stack_offset = stack_offset };
2695 },2715 },
26962716
...@@ -2698,13 +2718,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2698,13 +2718,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2698 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },2718 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
2699 .memory => |vaddr| return MCValue{ .immediate = vaddr },2719 .memory => |vaddr| return MCValue{ .immediate = vaddr },
27002720
2701 .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),2721 .undef => return self.fail("TODO implement ref on an undefined value", .{}),
2702 }2722 }
2703 }2723 }
27042724
2705 fn ret(self: *Self, src: LazySrcLoc, mcv: MCValue) !MCValue {2725 fn ret(self: *Self, mcv: MCValue) !MCValue {
2706 const ret_ty = self.fn_type.fnReturnType();2726 const ret_ty = self.fn_type.fnReturnType();
2707 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);2727 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
2708 switch (arch) {2728 switch (arch) {
2709 .i386 => {2729 .i386 => {
2710 try self.code.append(0xc3); // ret2730 try self.code.append(0xc3); // ret
...@@ -2730,58 +2750,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2730,58 +2750,54 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2730 try self.code.resize(self.code.items.len + 4);2750 try self.code.resize(self.code.items.len + 4);
2731 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);2751 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
2732 },2752 },
2733 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),2753 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
2734 }2754 }
2735 return .unreach;2755 return .unreach;
2736 }2756 }
27372757
2738 fn genRet(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2758 fn genRet(self: *Self, inst: Air.Inst.Index) !MCValue {
2739 const operand = try self.resolveInst(inst.operand);2759 const operand = try self.resolveInst(self.air.instructions.items(.data)[inst].un_op);
2740 return self.ret(inst.base.src, operand);2760 return self.ret(inst.base.src, operand);
2741 }2761 }
27422762
2743 fn genRetVoid(self: *Self, inst: *ir.Inst.NoOp) !MCValue {2763 fn genCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !MCValue {
2744 return self.ret(inst.base.src, .none);
2745 }
2746
2747 fn genCmp(self: *Self, inst: *ir.Inst.BinOp, op: math.CompareOperator) !MCValue {
2748 // No side effects, so if it's unreferenced, do nothing.2764 // No side effects, so if it's unreferenced, do nothing.
2749 if (inst.base.isUnused())2765 if (self.liveness.isUnused(inst))
2750 return MCValue{ .dead = {} };2766 return MCValue.dead;
2751 if (inst.lhs.ty.zigTypeTag() == .ErrorSet or inst.rhs.ty.zigTypeTag() == .ErrorSet)2767 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2752 return self.fail(inst.base.src, "TODO implement cmp for errors", .{});2768 const ty = self.air.getType(bin_op.lhs);
2769 assert(ty.eql(self.air.getType(bin_op.rhs)));
2770 if (ty.zigTypeTag() == .ErrorSet)
2771 return self.fail("TODO implement cmp for errors", .{});
2772
2773 const lhs = try self.resolveInst(bin_op.lhs);
2774 const rhs = try self.resolveInst(bin_op.rhs);
2753 switch (arch) {2775 switch (arch) {
2754 .x86_64 => {2776 .x86_64 => {
2755 try self.code.ensureCapacity(self.code.items.len + 8);2777 try self.code.ensureCapacity(self.code.items.len + 8);
27562778
2757 const lhs = try self.resolveInst(inst.lhs);
2758 const rhs = try self.resolveInst(inst.rhs);
2759
2760 // There are 2 operands, destination and source.2779 // There are 2 operands, destination and source.
2761 // Either one, but not both, can be a memory operand.2780 // Either one, but not both, can be a memory operand.
2762 // Source operand can be an immediate, 8 bits or 32 bits.2781 // Source operand can be an immediate, 8 bits or 32 bits.
2763 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))2782 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
2764 try self.copyToNewRegister(&inst.base, lhs)2783 try self.copyToNewRegister(inst, lhs)
2765 else2784 else
2766 lhs;2785 lhs;
2767 // This instruction supports only signed 32-bit immediates at most.2786 // This instruction supports only signed 32-bit immediates at most.
2768 const src_mcv = try self.limitImmediateType(inst.rhs, i32);2787 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
27692788
2770 try self.genX8664BinMathCode(inst.base.src, inst.base.ty, dst_mcv, src_mcv, 7, 0x38);2789 try self.genX8664BinMathCode(Type.initTag(.bool), dst_mcv, src_mcv, 7, 0x38);
2771 const info = inst.lhs.ty.intInfo(self.target.*);2790 const info = ty.intInfo(self.target.*);
2772 return switch (info.signedness) {2791 return switch (info.signedness) {
2773 .signed => MCValue{ .compare_flags_signed = op },2792 .signed => MCValue{ .compare_flags_signed = op },
2774 .unsigned => MCValue{ .compare_flags_unsigned = op },2793 .unsigned => MCValue{ .compare_flags_unsigned = op },
2775 };2794 };
2776 },2795 },
2777 .arm, .armeb => {2796 .arm, .armeb => {
2778 const lhs = try self.resolveInst(inst.lhs);
2779 const rhs = try self.resolveInst(inst.rhs);
2780
2781 const lhs_is_register = lhs == .register;2797 const lhs_is_register = lhs == .register;
2782 const rhs_is_register = rhs == .register;2798 const rhs_is_register = rhs == .register;
2783 // lhs should always be a register2799 // lhs should always be a register
2784 const rhs_should_be_register = try self.armOperandShouldBeRegister(inst.rhs.src, rhs);2800 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
27852801
2786 var lhs_mcv = lhs;2802 var lhs_mcv = lhs;
2787 var rhs_mcv = rhs;2803 var rhs_mcv = rhs;
...@@ -2789,49 +2805,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2789,49 +2805,55 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2789 // Allocate registers2805 // Allocate registers
2790 if (rhs_should_be_register) {2806 if (rhs_should_be_register) {
2791 if (!lhs_is_register and !rhs_is_register) {2807 if (!lhs_is_register and !rhs_is_register) {
2792 const regs = try self.register_manager.allocRegs(2, .{ inst.rhs, inst.lhs }, &.{});2808 const regs = try self.register_manager.allocRegs(2, .{ bin_op.rhs, bin_op.lhs }, &.{});
2793 lhs_mcv = MCValue{ .register = regs[0] };2809 lhs_mcv = MCValue{ .register = regs[0] };
2794 rhs_mcv = MCValue{ .register = regs[1] };2810 rhs_mcv = MCValue{ .register = regs[1] };
2795 } else if (!rhs_is_register) {2811 } else if (!rhs_is_register) {
2796 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.rhs, &.{}) };2812 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(bin_op.rhs, &.{}) };
2797 }2813 }
2798 }2814 }
2799 if (!lhs_is_register) {2815 if (!lhs_is_register) {
2800 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(inst.lhs, &.{}) };2816 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(bin_op.lhs, &.{}) };
2801 }2817 }
28022818
2803 // Move the operands to the newly allocated registers2819 // Move the operands to the newly allocated registers
2804 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];2820 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2805 if (lhs_mcv == .register and !lhs_is_register) {2821 if (lhs_mcv == .register and !lhs_is_register) {
2806 try self.genSetReg(inst.lhs.src, inst.lhs.ty, lhs_mcv.register, lhs);2822 try self.genSetReg(ty, lhs_mcv.register, lhs);
2807 branch.inst_table.putAssumeCapacity(inst.lhs, lhs);2823 branch.inst_table.putAssumeCapacity(bin_op.lhs, lhs);
2808 }2824 }
2809 if (rhs_mcv == .register and !rhs_is_register) {2825 if (rhs_mcv == .register and !rhs_is_register) {
2810 try self.genSetReg(inst.rhs.src, inst.rhs.ty, rhs_mcv.register, rhs);2826 try self.genSetReg(ty, rhs_mcv.register, rhs);
2811 branch.inst_table.putAssumeCapacity(inst.rhs, rhs);2827 branch.inst_table.putAssumeCapacity(bin_op.rhs, rhs);
2812 }2828 }
28132829
2814 // The destination register is not present in the cmp instruction2830 // The destination register is not present in the cmp instruction
2815 try self.genArmBinOpCode(inst.base.src, undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);2831 try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq);
28162832
2817 const info = inst.lhs.ty.intInfo(self.target.*);2833 const info = ty.intInfo(self.target.*);
2818 return switch (info.signedness) {2834 return switch (info.signedness) {
2819 .signed => MCValue{ .compare_flags_signed = op },2835 .signed => MCValue{ .compare_flags_signed = op },
2820 .unsigned => MCValue{ .compare_flags_unsigned = op },2836 .unsigned => MCValue{ .compare_flags_unsigned = op },
2821 };2837 };
2822 },2838 },
2823 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),2839 else => return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}),
2824 }2840 }
2825 }2841 }
28262842
2827 fn genDbgStmt(self: *Self, inst: *ir.Inst.DbgStmt) !MCValue {2843 fn genDbgStmt(self: *Self, inst: Air.Inst.Index) !MCValue {
2828 try self.dbgAdvancePCAndLine(inst.line, inst.column);2844 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
2829 assert(inst.base.isUnused());2845 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
2846 assert(self.liveness.isUnused(inst));
2830 return MCValue.dead;2847 return MCValue.dead;
2831 }2848 }
28322849
2833 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {2850 fn genCondBr(self: *Self, inst: Air.Inst.Index) !MCValue {
2834 const cond = try self.resolveInst(inst.condition);2851 const inst_datas = self.air.instructions.items(.data);
2852 const pl_op = inst_datas[inst].pl_op;
2853 const cond = try self.resolveInst(pl_op.operand);
2854 const extra = self.air.extraData(Air.CondBr, inst_data.payload);
2855 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
2856 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
28352857
2836 const reloc: Reloc = switch (arch) {2858 const reloc: Reloc = switch (arch) {
2837 .i386, .x86_64 => reloc: {2859 .i386, .x86_64 => reloc: {
...@@ -2880,7 +2902,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2880,7 +2902,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2880 encoder.disp8(1);2902 encoder.disp8(1);
2881 break :blk 0x84;2903 break :blk 0x84;
2882 },2904 },
2883 else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),2905 else => return self.fail("TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
2884 };2906 };
2885 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });2907 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
2886 const reloc = Reloc{ .rel32 = self.code.items.len };2908 const reloc = Reloc{ .rel32 = self.code.items.len };
...@@ -2906,7 +2928,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2906,7 +2928,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2906 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());2928 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32());
2907 break :blk .ne;2929 break :blk .ne;
2908 },2930 },
2909 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),2931 else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }),
2910 };2932 };
29112933
2912 const reloc = Reloc{2934 const reloc = Reloc{
...@@ -2918,7 +2940,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2918,7 +2940,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2918 try self.code.resize(self.code.items.len + 4);2940 try self.code.resize(self.code.items.len + 4);
2919 break :reloc reloc;2941 break :reloc reloc;
2920 },2942 },
2921 else => return self.fail(inst.base.src, "TODO implement condbr {}", .{self.target.cpu.arch}),2943 else => return self.fail("TODO implement condbr {}", .{self.target.cpu.arch}),
2922 };2944 };
29232945
2924 // Capture the state of register and stack allocation state so that we can revert to it.2946 // Capture the state of register and stack allocation state so that we can revert to it.
...@@ -2930,12 +2952,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2930,12 +2952,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29302952
2931 try self.branch_stack.append(.{});2953 try self.branch_stack.append(.{});
29322954
2933 const then_deaths = inst.thenDeaths();2955 const then_deaths = self.liveness.thenDeaths(inst);
2934 try self.ensureProcessDeathCapacity(then_deaths.len);2956 try self.ensureProcessDeathCapacity(then_deaths.len);
2935 for (then_deaths) |operand| {2957 for (then_deaths) |operand| {
2936 self.processDeath(operand);2958 self.processDeath(operand);
2937 }2959 }
2938 try self.genBody(inst.then_body);2960 try self.genBody(then_body);
29392961
2940 // Revert to the previous register and stack allocation state.2962 // Revert to the previous register and stack allocation state.
29412963
...@@ -2951,16 +2973,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2951,16 +2973,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2951 self.next_stack_offset = parent_next_stack_offset;2973 self.next_stack_offset = parent_next_stack_offset;
2952 self.register_manager.free_registers = parent_free_registers;2974 self.register_manager.free_registers = parent_free_registers;
29532975
2954 try self.performReloc(inst.base.src, reloc);2976 try self.performReloc(reloc);
2955 const else_branch = self.branch_stack.addOneAssumeCapacity();2977 const else_branch = self.branch_stack.addOneAssumeCapacity();
2956 else_branch.* = .{};2978 else_branch.* = .{};
29572979
2958 const else_deaths = inst.elseDeaths();2980 const else_deaths = self.liveness.elseDeaths(inst);
2959 try self.ensureProcessDeathCapacity(else_deaths.len);2981 try self.ensureProcessDeathCapacity(else_deaths.len);
2960 for (else_deaths) |operand| {2982 for (else_deaths) |operand| {
2961 self.processDeath(operand);2983 self.processDeath(operand);
2962 }2984 }
2963 try self.genBody(inst.else_body);2985 try self.genBody(else_body);
29642986
2965 // At this point, each branch will possibly have conflicting values for where2987 // At this point, each branch will possibly have conflicting values for where
2966 // each instruction is stored. They agree, however, on which instructions are alive/dead.2988 // each instruction is stored. They agree, however, on which instructions are alive/dead.
...@@ -3003,7 +3025,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3003,7 +3025,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3003 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });3025 log.debug("consolidating else_entry {*} {}=>{}", .{ else_key, else_value, canon_mcv });
3004 // TODO make sure the destination stack offset / register does not already have something3026 // TODO make sure the destination stack offset / register does not already have something
3005 // going on there.3027 // going on there.
3006 try self.setRegOrMem(inst.base.src, else_key.ty, canon_mcv, else_value);3028 try self.setRegOrMem(else_key.ty, canon_mcv, else_value);
3007 // TODO track the new register / stack allocation3029 // TODO track the new register / stack allocation
3008 }3030 }
3009 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +3031 try parent_branch.inst_table.ensureCapacity(self.gpa, parent_branch.inst_table.count() +
...@@ -3031,7 +3053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3031,7 +3053,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3031 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });3053 log.debug("consolidating then_entry {*} {}=>{}", .{ then_key, parent_mcv, then_value });
3032 // TODO make sure the destination stack offset / register does not already have something3054 // TODO make sure the destination stack offset / register does not already have something
3033 // going on there.3055 // going on there.
3034 try self.setRegOrMem(inst.base.src, then_key.ty, parent_mcv, then_value);3056 try self.setRegOrMem(then_key.ty, parent_mcv, then_value);
3035 // TODO track the new register / stack allocation3057 // TODO track the new register / stack allocation
3036 }3058 }
30373059
...@@ -3040,58 +3062,155 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3040,58 +3062,155 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3040 return MCValue.unreach;3062 return MCValue.unreach;
3041 }3063 }
30423064
3043 fn genIsNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3065 fn isNull(self: *Self, operand: MCValue) !MCValue {
3066 _ = operand;
3067 // Here you can specialize this instruction if it makes sense to, otherwise the default
3068 // will call isNonNull and invert the result.
3044 switch (arch) {3069 switch (arch) {
3045 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),3070 else => return self.fail("TODO call isNonNull and invert the result", .{}),
3046 }3071 }
3047 }3072 }
30483073
3049 fn genIsNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3074 fn isNonNull(self: *Self, operand: MCValue) !MCValue {
3050 return self.fail(inst.base.src, "TODO load the operand and call genIsNull", .{});3075 _ = operand;
3051 }
3052
3053 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
3054 // Here you can specialize this instruction if it makes sense to, otherwise the default3076 // Here you can specialize this instruction if it makes sense to, otherwise the default
3055 // will call genIsNull and invert the result.3077 // will call isNull and invert the result.
3056 switch (arch) {3078 switch (arch) {
3057 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),3079 else => return self.fail("TODO call isNull and invert the result", .{}),
3058 }3080 }
3059 }3081 }
30603082
3061 fn genIsNonNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3083 fn isErr(self: *Self, operand: MCValue) !MCValue {
3062 return self.fail(inst.base.src, "TODO load the operand and call genIsNonNull", .{});3084 _ = operand;
3085 // Here you can specialize this instruction if it makes sense to, otherwise the default
3086 // will call isNonNull and invert the result.
3087 switch (arch) {
3088 else => return self.fail("TODO call isNonErr and invert the result", .{}),
3089 }
3063 }3090 }
30643091
3065 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3092 fn isNonErr(self: *Self, operand: MCValue) !MCValue {
3093 _ = operand;
3094 // Here you can specialize this instruction if it makes sense to, otherwise the default
3095 // will call isNull and invert the result.
3066 switch (arch) {3096 switch (arch) {
3067 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),3097 else => return self.fail("TODO call isErr and invert the result", .{}),
3068 }3098 }
3069 }3099 }
30703100
3071 fn genIsErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3101 fn genIsNull(self: *Self, inst: Air.Inst.Index) !MCValue {
3072 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});3102 if (self.liveness.isUnused(inst))
3103 return MCValue.dead;
3104 const inst_datas = self.air.instructions.items(.data);
3105 const operand = try self.resolveInst(inst_datas[inst].un_op);
3106 return self.isNull(operand);
3073 }3107 }
30743108
3075 fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3109 fn genIsNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3076 switch (arch) {3110 if (self.liveness.isUnused(inst))
3077 else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),3111 return MCValue.dead;
3078 }3112 const inst_datas = self.air.instructions.items(.data);
3113 const operand_ptr = try self.resolveInst(inst_datas[inst].un_op);
3114 const operand: MCValue = blk: {
3115 if (self.reuseOperand(inst, 0, operand_ptr)) {
3116 // The MCValue that holds the pointer can be re-used as the value.
3117 break :blk operand_ptr;
3118 } else {
3119 break :blk try self.allocRegOrMem(inst, true);
3120 }
3121 };
3122 try self.load(operand, ptr);
3123 return self.isNull(operand);
3079 }3124 }
30803125
3081 fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {3126 fn genIsNonNull(self: *Self, inst: Air.Inst.Index) !MCValue {
3082 return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});3127 if (self.liveness.isUnused(inst))
3128 return MCValue.dead;
3129 const inst_datas = self.air.instructions.items(.data);
3130 const operand = try self.resolveInst(inst_datas[inst].un_op);
3131 return self.isNonNull(operand);
3083 }3132 }
30843133
3085 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {3134 fn genIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3135 if (self.liveness.isUnused(inst))
3136 return MCValue.dead;
3137 const inst_datas = self.air.instructions.items(.data);
3138 const operand_ptr = try self.resolveInst(inst_datas[inst].un_op);
3139 const operand: MCValue = blk: {
3140 if (self.reuseOperand(inst, 0, operand_ptr)) {
3141 // The MCValue that holds the pointer can be re-used as the value.
3142 break :blk operand_ptr;
3143 } else {
3144 break :blk try self.allocRegOrMem(inst, true);
3145 }
3146 };
3147 try self.load(operand, ptr);
3148 return self.isNonNull(operand);
3149 }
3150
3151 fn genIsErr(self: *Self, inst: Air.Inst.Index) !MCValue {
3152 if (self.liveness.isUnused(inst))
3153 return MCValue.dead;
3154 const inst_datas = self.air.instructions.items(.data);
3155 const operand = try self.resolveInst(inst_datas[inst].un_op);
3156 return self.isErr(operand);
3157 }
3158
3159 fn genIsErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3160 if (self.liveness.isUnused(inst))
3161 return MCValue.dead;
3162 const inst_datas = self.air.instructions.items(.data);
3163 const operand_ptr = try self.resolveInst(inst_datas[inst].un_op);
3164 const operand: MCValue = blk: {
3165 if (self.reuseOperand(inst, 0, operand_ptr)) {
3166 // The MCValue that holds the pointer can be re-used as the value.
3167 break :blk operand_ptr;
3168 } else {
3169 break :blk try self.allocRegOrMem(inst, true);
3170 }
3171 };
3172 try self.load(operand, ptr);
3173 return self.isErr(operand);
3174 }
3175
3176 fn genIsNonErr(self: *Self, inst: Air.Inst.Index) !MCValue {
3177 if (self.liveness.isUnused(inst))
3178 return MCValue.dead;
3179 const inst_datas = self.air.instructions.items(.data);
3180 const operand = try self.resolveInst(inst_datas[inst].un_op);
3181 return self.isNonErr(operand);
3182 }
3183
3184 fn genIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !MCValue {
3185 if (self.liveness.isUnused(inst))
3186 return MCValue.dead;
3187 const inst_datas = self.air.instructions.items(.data);
3188 const operand_ptr = try self.resolveInst(inst_datas[inst].un_op);
3189 const operand: MCValue = blk: {
3190 if (self.reuseOperand(inst, 0, operand_ptr)) {
3191 // The MCValue that holds the pointer can be re-used as the value.
3192 break :blk operand_ptr;
3193 } else {
3194 break :blk try self.allocRegOrMem(inst, true);
3195 }
3196 };
3197 try self.load(operand, ptr);
3198 return self.isNonErr(operand);
3199 }
3200
3201 fn genLoop(self: *Self, inst: Air.Inst.Index) !MCValue {
3086 // A loop is a setup to be able to jump back to the beginning.3202 // A loop is a setup to be able to jump back to the beginning.
3203 const inst_datas = self.air.instructions.items(.data);
3204 const loop = self.air.extraData(Air.Block, inst_datas[inst].ty_pl.payload);
3205 const body = self.air.extra[loop.end..][0..loop.data.body_len];
3087 const start_index = self.code.items.len;3206 const start_index = self.code.items.len;
3088 try self.genBody(inst.body);3207 try self.genBody(body);
3089 try self.jump(inst.base.src, start_index);3208 try self.jump(start_index);
3090 return MCValue.unreach;3209 return MCValue.unreach;
3091 }3210 }
30923211
3093 /// Send control flow to the `index` of `self.code`.3212 /// Send control flow to the `index` of `self.code`.
3094 fn jump(self: *Self, src: LazySrcLoc, index: usize) !void {3213 fn jump(self: *Self, index: usize) !void {
3095 switch (arch) {3214 switch (arch) {
3096 .i386, .x86_64 => {3215 .i386, .x86_64 => {
3097 try self.code.ensureCapacity(self.code.items.len + 5);3216 try self.code.ensureCapacity(self.code.items.len + 5);
...@@ -3108,21 +3227,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3108,21 +3227,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3108 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {3227 if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
3109 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());3228 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32());
3110 } else |_| {3229 } else |_| {
3111 return self.fail(src, "TODO: enable larger branch offset", .{});3230 return self.fail("TODO: enable larger branch offset", .{});
3112 }3231 }
3113 },3232 },
3114 .aarch64, .aarch64_be, .aarch64_32 => {3233 .aarch64, .aarch64_be, .aarch64_32 => {
3115 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {3234 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
3116 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());3235 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
3117 } else |_| {3236 } else |_| {
3118 return self.fail(src, "TODO: enable larger branch offset", .{});3237 return self.fail("TODO: enable larger branch offset", .{});
3119 }3238 }
3120 },3239 },
3121 else => return self.fail(src, "TODO implement jump for {}", .{self.target.cpu.arch}),3240 else => return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}),
3122 }3241 }
3123 }3242 }
31243243
3125 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {3244 fn genBlock(self: *Self, inst: Air.Inst.Index) !MCValue {
3126 try self.blocks.putNoClobber(self.gpa, inst, .{3245 try self.blocks.putNoClobber(self.gpa, inst, .{
3127 // A block is a setup to be able to jump to the end.3246 // A block is a setup to be able to jump to the end.
3128 .relocs = .{},3247 .relocs = .{},
...@@ -3136,20 +3255,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3136,20 +3255,24 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3136 const block_data = self.blocks.getPtr(inst).?;3255 const block_data = self.blocks.getPtr(inst).?;
3137 defer block_data.relocs.deinit(self.gpa);3256 defer block_data.relocs.deinit(self.gpa);
31383257
3139 try self.genBody(inst.body);3258 const ty_pl = self.air.instructions.items(.data).ty_pl;
3259 const extra = self.air.extraData(Air.Block, ty_pl.payload);
3260 const body = self.air.extra[extra.end..][0..extra.data.body_len];
3261 try self.genBody(body);
31403262
3141 for (block_data.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);3263 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
31423264
3143 return @bitCast(MCValue, block_data.mcv);3265 return @bitCast(MCValue, block_data.mcv);
3144 }3266 }
31453267
3146 fn genSwitch(self: *Self, inst: *ir.Inst.SwitchBr) !MCValue {3268 fn genSwitch(self: *Self, inst: Air.Inst.Index) !MCValue {
3269 _ = inst;
3147 switch (arch) {3270 switch (arch) {
3148 else => return self.fail(inst.base.src, "TODO genSwitch for {}", .{self.target.cpu.arch}),3271 else => return self.fail("TODO genSwitch for {}", .{self.target.cpu.arch}),
3149 }3272 }
3150 }3273 }
31513274
3152 fn performReloc(self: *Self, src: LazySrcLoc, reloc: Reloc) !void {3275 fn performReloc(self: *Self, reloc: Reloc) !void {
3153 switch (reloc) {3276 switch (reloc) {
3154 .rel32 => |pos| {3277 .rel32 => |pos| {
3155 const amt = self.code.items.len - (pos + 4);3278 const amt = self.code.items.len - (pos + 4);
...@@ -3160,7 +3283,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3160,7 +3283,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3160 // best place to elide jumps will be in semantic analysis, by inlining blocks that only3283 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
3161 // only have 1 break instruction.3284 // only have 1 break instruction.
3162 const s32_amt = math.cast(i32, amt) catch3285 const s32_amt = math.cast(i32, amt) catch
3163 return self.fail(src, "unable to perform relocation: jump too far", .{});3286 return self.fail("unable to perform relocation: jump too far", .{});
3164 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);3287 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
3165 },3288 },
3166 .arm_branch => |info| {3289 .arm_branch => |info| {
...@@ -3170,7 +3293,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3170,7 +3293,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3170 if (math.cast(i26, amt)) |delta| {3293 if (math.cast(i26, amt)) |delta| {
3171 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());3294 writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32());
3172 } else |_| {3295 } else |_| {
3173 return self.fail(src, "TODO: enable larger branch offset", .{});3296 return self.fail("TODO: enable larger branch offset", .{});
3174 }3297 }
3175 },3298 },
3176 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch3299 else => unreachable, // attempting to perfrom an ARM relocation on a non-ARM target arch
...@@ -3179,41 +3302,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3179,41 +3302,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3179 }3302 }
3180 }3303 }
31813304
3182 fn genBrBlockFlat(self: *Self, inst: *ir.Inst.BrBlockFlat) !MCValue {3305 fn genBrBlockFlat(self: *Self, inst: Air.Inst.Index) !MCValue {
3183 try self.genBody(inst.body);3306 try self.genBody(inst.body);
3184 const last = inst.body.instructions[inst.body.instructions.len - 1];3307 const last = inst.body.instructions[inst.body.instructions.len - 1];
3185 return self.br(inst.base.src, inst.block, last);3308 return self.br(inst.block, last);
3186 }
3187
3188 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
3189 return self.br(inst.base.src, inst.block, inst.operand);
3190 }3309 }
31913310
3192 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {3311 fn genBr(self: *Self, inst: Air.Inst.Index) !MCValue {
3193 return self.brVoid(inst.base.src, inst.block);3312 return self.br(inst.block, inst.operand);
3194 }3313 }
31953314
3196 fn genBoolOp(self: *Self, inst: *ir.Inst.BinOp) !MCValue {3315 fn genBoolOp(self: *Self, inst: Air.Inst.Index) !MCValue {
3197 if (inst.base.isUnused())3316 if (self.liveness.isUnused(inst))
3198 return MCValue.dead;3317 return MCValue.dead;
3318 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3319 const air_tags = self.air.instructions.items(.tag);
3199 switch (arch) {3320 switch (arch) {
3200 .x86_64 => switch (inst.base.tag) {3321 .x86_64 => switch (air_tags[inst]) {
3201 // lhs AND rhs3322 // lhs AND rhs
3202 .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),3323 .bool_and => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3203 // lhs OR rhs3324 // lhs OR rhs
3204 .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs),3325 .bool_or => return try self.genX8664BinMath(inst, bin_op.lhs, bin_op.rhs),
3205 else => unreachable, // Not a boolean operation3326 else => unreachable, // Not a boolean operation
3206 },3327 },
3207 .arm, .armeb => switch (inst.base.tag) {3328 .arm, .armeb => switch (air_tags[inst]) {
3208 .bool_and => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_and),3329 .bool_and => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_and),
3209 .bool_or => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .bool_or),3330 .bool_or => return try self.genArmBinOp(inst, bin_op.lhs, bin_op.rhs, .bool_or),
3210 else => unreachable, // Not a boolean operation3331 else => unreachable, // Not a boolean operation
3211 },3332 },
3212 else => return self.fail(inst.base.src, "TODO implement boolean operations for {}", .{self.target.cpu.arch}),3333 else => return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}),
3213 }3334 }
3214 }3335 }
32153336
3216 fn br(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block, operand: *ir.Inst) !MCValue {3337 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Index) !MCValue {
3217 const block_data = self.blocks.getPtr(block).?;3338 const block_data = self.blocks.getPtr(block).?;
32183339
3219 if (operand.ty.hasCodeGenBits()) {3340 if (operand.ty.hasCodeGenBits()) {
...@@ -3222,13 +3343,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3222,13 +3343,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3222 if (block_mcv == .none) {3343 if (block_mcv == .none) {
3223 block_data.mcv = operand_mcv;3344 block_data.mcv = operand_mcv;
3224 } else {3345 } else {
3225 try self.setRegOrMem(src, block.base.ty, block_mcv, operand_mcv);3346 try self.setRegOrMem(block.base.ty, block_mcv, operand_mcv);
3226 }3347 }
3227 }3348 }
3228 return self.brVoid(src, block);3349 return self.brVoid(block);
3229 }3350 }
32303351
3231 fn brVoid(self: *Self, src: LazySrcLoc, block: *ir.Inst.Block) !MCValue {3352 fn brVoid(self: *Self, block: Air.Inst.Index) !MCValue {
3232 const block_data = self.blocks.getPtr(block).?;3353 const block_data = self.blocks.getPtr(block).?;
32333354
3234 // Emit a jump with a relocation. It will be patched up after the block ends.3355 // Emit a jump with a relocation. It will be patched up after the block ends.
...@@ -3252,43 +3373,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3252,43 +3373,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3252 },3373 },
3253 });3374 });
3254 },3375 },
3255 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),3376 else => return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}),
3256 }3377 }
3257 return .none;3378 return .none;
3258 }3379 }
32593380
3260 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {3381 fn genAsm(self: *Self, inst: Air.Inst.Index) !MCValue {
3261 if (!inst.is_volatile and inst.base.isUnused())3382 if (!inst.is_volatile and self.liveness.isUnused(inst))
3262 return MCValue.dead;3383 return MCValue.dead;
3263 switch (arch) {3384 switch (arch) {
3264 .arm, .armeb => {3385 .arm, .armeb => {
3265 for (inst.inputs) |input, i| {3386 for (inst.inputs) |input, i| {
3266 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3387 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3267 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3388 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3268 }3389 }
3269 const reg_name = input[1 .. input.len - 1];3390 const reg_name = input[1 .. input.len - 1];
3270 const reg = parseRegName(reg_name) orelse3391 const reg = parseRegName(reg_name) orelse
3271 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3392 return self.fail("unrecognized register: '{s}'", .{reg_name});
32723393
3273 const arg = inst.args[i];3394 const arg = inst.args[i];
3274 const arg_mcv = try self.resolveInst(arg);3395 const arg_mcv = try self.resolveInst(arg);
3275 try self.register_manager.getReg(reg, null);3396 try self.register_manager.getReg(reg, null);
3276 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3397 try self.genSetReg(arg.ty, reg, arg_mcv);
3277 }3398 }
32783399
3279 if (mem.eql(u8, inst.asm_source, "svc #0")) {3400 if (mem.eql(u8, inst.asm_source, "svc #0")) {
3280 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());3401 writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32());
3281 } else {3402 } else {
3282 return self.fail(inst.base.src, "TODO implement support for more arm assembly instructions", .{});3403 return self.fail("TODO implement support for more arm assembly instructions", .{});
3283 }3404 }
32843405
3285 if (inst.output_constraint) |output| {3406 if (inst.output_constraint) |output| {
3286 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3407 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3287 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3408 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3288 }3409 }
3289 const reg_name = output[2 .. output.len - 1];3410 const reg_name = output[2 .. output.len - 1];
3290 const reg = parseRegName(reg_name) orelse3411 const reg = parseRegName(reg_name) orelse
3291 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3412 return self.fail("unrecognized register: '{s}'", .{reg_name});
3292 return MCValue{ .register = reg };3413 return MCValue{ .register = reg };
3293 } else {3414 } else {
3294 return MCValue.none;3415 return MCValue.none;
...@@ -3297,16 +3418,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3297,16 +3418,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3297 .aarch64 => {3418 .aarch64 => {
3298 for (inst.inputs) |input, i| {3419 for (inst.inputs) |input, i| {
3299 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3420 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3300 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3421 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3301 }3422 }
3302 const reg_name = input[1 .. input.len - 1];3423 const reg_name = input[1 .. input.len - 1];
3303 const reg = parseRegName(reg_name) orelse3424 const reg = parseRegName(reg_name) orelse
3304 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3425 return self.fail("unrecognized register: '{s}'", .{reg_name});
33053426
3306 const arg = inst.args[i];3427 const arg = inst.args[i];
3307 const arg_mcv = try self.resolveInst(arg);3428 const arg_mcv = try self.resolveInst(arg);
3308 try self.register_manager.getReg(reg, null);3429 try self.register_manager.getReg(reg, null);
3309 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3430 try self.genSetReg(arg.ty, reg, arg_mcv);
3310 }3431 }
33113432
3312 if (mem.eql(u8, inst.asm_source, "svc #0")) {3433 if (mem.eql(u8, inst.asm_source, "svc #0")) {
...@@ -3314,16 +3435,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3314,16 +3435,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3314 } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {3435 } else if (mem.eql(u8, inst.asm_source, "svc #0x80")) {
3315 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());3436 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
3316 } else {3437 } else {
3317 return self.fail(inst.base.src, "TODO implement support for more aarch64 assembly instructions", .{});3438 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
3318 }3439 }
33193440
3320 if (inst.output_constraint) |output| {3441 if (inst.output_constraint) |output| {
3321 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3442 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3322 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3443 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3323 }3444 }
3324 const reg_name = output[2 .. output.len - 1];3445 const reg_name = output[2 .. output.len - 1];
3325 const reg = parseRegName(reg_name) orelse3446 const reg = parseRegName(reg_name) orelse
3326 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3447 return self.fail("unrecognized register: '{s}'", .{reg_name});
3327 return MCValue{ .register = reg };3448 return MCValue{ .register = reg };
3328 } else {3449 } else {
3329 return MCValue.none;3450 return MCValue.none;
...@@ -3332,31 +3453,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3332,31 +3453,31 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3332 .riscv64 => {3453 .riscv64 => {
3333 for (inst.inputs) |input, i| {3454 for (inst.inputs) |input, i| {
3334 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3455 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3335 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3456 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3336 }3457 }
3337 const reg_name = input[1 .. input.len - 1];3458 const reg_name = input[1 .. input.len - 1];
3338 const reg = parseRegName(reg_name) orelse3459 const reg = parseRegName(reg_name) orelse
3339 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3460 return self.fail("unrecognized register: '{s}'", .{reg_name});
33403461
3341 const arg = inst.args[i];3462 const arg = inst.args[i];
3342 const arg_mcv = try self.resolveInst(arg);3463 const arg_mcv = try self.resolveInst(arg);
3343 try self.register_manager.getReg(reg, null);3464 try self.register_manager.getReg(reg, null);
3344 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3465 try self.genSetReg(arg.ty, reg, arg_mcv);
3345 }3466 }
33463467
3347 if (mem.eql(u8, inst.asm_source, "ecall")) {3468 if (mem.eql(u8, inst.asm_source, "ecall")) {
3348 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());3469 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
3349 } else {3470 } else {
3350 return self.fail(inst.base.src, "TODO implement support for more riscv64 assembly instructions", .{});3471 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
3351 }3472 }
33523473
3353 if (inst.output_constraint) |output| {3474 if (inst.output_constraint) |output| {
3354 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3475 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3355 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3476 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3356 }3477 }
3357 const reg_name = output[2 .. output.len - 1];3478 const reg_name = output[2 .. output.len - 1];
3358 const reg = parseRegName(reg_name) orelse3479 const reg = parseRegName(reg_name) orelse
3359 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3480 return self.fail("unrecognized register: '{s}'", .{reg_name});
3360 return MCValue{ .register = reg };3481 return MCValue{ .register = reg };
3361 } else {3482 } else {
3362 return MCValue.none;3483 return MCValue.none;
...@@ -3365,16 +3486,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3365,16 +3486,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3365 .x86_64, .i386 => {3486 .x86_64, .i386 => {
3366 for (inst.inputs) |input, i| {3487 for (inst.inputs) |input, i| {
3367 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {3488 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
3368 return self.fail(inst.base.src, "unrecognized asm input constraint: '{s}'", .{input});3489 return self.fail("unrecognized asm input constraint: '{s}'", .{input});
3369 }3490 }
3370 const reg_name = input[1 .. input.len - 1];3491 const reg_name = input[1 .. input.len - 1];
3371 const reg = parseRegName(reg_name) orelse3492 const reg = parseRegName(reg_name) orelse
3372 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3493 return self.fail("unrecognized register: '{s}'", .{reg_name});
33733494
3374 const arg = inst.args[i];3495 const arg = inst.args[i];
3375 const arg_mcv = try self.resolveInst(arg);3496 const arg_mcv = try self.resolveInst(arg);
3376 try self.register_manager.getReg(reg, null);3497 try self.register_manager.getReg(reg, null);
3377 try self.genSetReg(inst.base.src, arg.ty, reg, arg_mcv);3498 try self.genSetReg(arg.ty, reg, arg_mcv);
3378 }3499 }
33793500
3380 {3501 {
...@@ -3385,68 +3506,68 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3385,68 +3506,68 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3385 } else if (mem.indexOf(u8, ins, "push")) |_| {3506 } else if (mem.indexOf(u8, ins, "push")) |_| {
3386 const arg = ins[4..];3507 const arg = ins[4..];
3387 if (mem.indexOf(u8, arg, "$")) |l| {3508 if (mem.indexOf(u8, arg, "$")) |l| {
3388 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail(inst.base.src, "TODO implement more inline asm int parsing", .{});3509 const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch return self.fail("TODO implement more inline asm int parsing", .{});
3389 try self.code.appendSlice(&.{ 0x6a, n });3510 try self.code.appendSlice(&.{ 0x6a, n });
3390 } else if (mem.indexOf(u8, arg, "%%")) |l| {3511 } else if (mem.indexOf(u8, arg, "%%")) |l| {
3391 const reg_name = ins[4 + l + 2 ..];3512 const reg_name = ins[4 + l + 2 ..];
3392 const reg = parseRegName(reg_name) orelse3513 const reg = parseRegName(reg_name) orelse
3393 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3514 return self.fail("unrecognized register: '{s}'", .{reg_name});
3394 const low_id: u8 = reg.low_id();3515 const low_id: u8 = reg.low_id();
3395 if (reg.isExtended()) {3516 if (reg.isExtended()) {
3396 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });3517 try self.code.appendSlice(&.{ 0x41, 0b1010000 | low_id });
3397 } else {3518 } else {
3398 try self.code.append(0b1010000 | low_id);3519 try self.code.append(0b1010000 | low_id);
3399 }3520 }
3400 } else return self.fail(inst.base.src, "TODO more push operands", .{});3521 } else return self.fail("TODO more push operands", .{});
3401 } else if (mem.indexOf(u8, ins, "pop")) |_| {3522 } else if (mem.indexOf(u8, ins, "pop")) |_| {
3402 const arg = ins[3..];3523 const arg = ins[3..];
3403 if (mem.indexOf(u8, arg, "%%")) |l| {3524 if (mem.indexOf(u8, arg, "%%")) |l| {
3404 const reg_name = ins[3 + l + 2 ..];3525 const reg_name = ins[3 + l + 2 ..];
3405 const reg = parseRegName(reg_name) orelse3526 const reg = parseRegName(reg_name) orelse
3406 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3527 return self.fail("unrecognized register: '{s}'", .{reg_name});
3407 const low_id: u8 = reg.low_id();3528 const low_id: u8 = reg.low_id();
3408 if (reg.isExtended()) {3529 if (reg.isExtended()) {
3409 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });3530 try self.code.appendSlice(&.{ 0x41, 0b1011000 | low_id });
3410 } else {3531 } else {
3411 try self.code.append(0b1011000 | low_id);3532 try self.code.append(0b1011000 | low_id);
3412 }3533 }
3413 } else return self.fail(inst.base.src, "TODO more pop operands", .{});3534 } else return self.fail("TODO more pop operands", .{});
3414 } else {3535 } else {
3415 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});3536 return self.fail("TODO implement support for more x86 assembly instructions", .{});
3416 }3537 }
3417 }3538 }
3418 }3539 }
34193540
3420 if (inst.output_constraint) |output| {3541 if (inst.output_constraint) |output| {
3421 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {3542 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3422 return self.fail(inst.base.src, "unrecognized asm output constraint: '{s}'", .{output});3543 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3423 }3544 }
3424 const reg_name = output[2 .. output.len - 1];3545 const reg_name = output[2 .. output.len - 1];
3425 const reg = parseRegName(reg_name) orelse3546 const reg = parseRegName(reg_name) orelse
3426 return self.fail(inst.base.src, "unrecognized register: '{s}'", .{reg_name});3547 return self.fail("unrecognized register: '{s}'", .{reg_name});
3427 return MCValue{ .register = reg };3548 return MCValue{ .register = reg };
3428 } else {3549 } else {
3429 return MCValue.none;3550 return MCValue.none;
3430 }3551 }
3431 },3552 },
3432 else => return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{}),3553 else => return self.fail("TODO implement inline asm support for more architectures", .{}),
3433 }3554 }
3434 }3555 }
34353556
3436 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.3557 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
3437 fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void {3558 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
3438 switch (loc) {3559 switch (loc) {
3439 .none => return,3560 .none => return,
3440 .register => |reg| return self.genSetReg(src, ty, reg, val),3561 .register => |reg| return self.genSetReg(ty, reg, val),
3441 .stack_offset => |off| return self.genSetStack(src, ty, off, val),3562 .stack_offset => |off| return self.genSetStack(ty, off, val),
3442 .memory => {3563 .memory => {
3443 return self.fail(src, "TODO implement setRegOrMem for memory", .{});3564 return self.fail("TODO implement setRegOrMem for memory", .{});
3444 },3565 },
3445 else => unreachable,3566 else => unreachable,
3446 }3567 }
3447 }3568 }
34483569
3449 fn genSetStack(self: *Self, src: LazySrcLoc, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {3570 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
3450 switch (arch) {3571 switch (arch) {
3451 .arm, .armeb => switch (mcv) {3572 .arm, .armeb => switch (mcv) {
3452 .dead => unreachable,3573 .dead => unreachable,
...@@ -3458,28 +3579,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3458,28 +3579,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3458 return; // The already existing value will do just fine.3579 return; // The already existing value will do just fine.
3459 // TODO Upgrade this to a memset call when we have that available.3580 // TODO Upgrade this to a memset call when we have that available.
3460 switch (ty.abiSize(self.target.*)) {3581 switch (ty.abiSize(self.target.*)) {
3461 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3582 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3462 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3583 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3463 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3584 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3464 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3585 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3465 else => return self.fail(src, "TODO implement memset", .{}),3586 else => return self.fail("TODO implement memset", .{}),
3466 }3587 }
3467 },3588 },
3468 .compare_flags_unsigned => |op| {3589 .compare_flags_unsigned => |op| {
3469 _ = op;3590 _ = op;
3470 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3591 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3471 },3592 },
3472 .compare_flags_signed => |op| {3593 .compare_flags_signed => |op| {
3473 _ = op;3594 _ = op;
3474 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3595 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3475 },3596 },
3476 .immediate => {3597 .immediate => {
3477 const reg = try self.copyToTmpRegister(src, ty, mcv);3598 const reg = try self.copyToTmpRegister(ty, mcv);
3478 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3599 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3479 },3600 },
3480 .embedded_in_code => |code_offset| {3601 .embedded_in_code => |code_offset| {
3481 _ = code_offset;3602 _ = code_offset;
3482 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});3603 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
3483 },3604 },
3484 .register => |reg| {3605 .register => |reg| {
3485 const abi_size = ty.abiSize(self.target.*);3606 const abi_size = ty.abiSize(self.target.*);
...@@ -3489,7 +3610,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3489,7 +3610,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3489 1, 4 => {3610 1, 4 => {
3490 const offset = if (math.cast(u12, adj_off)) |imm| blk: {3611 const offset = if (math.cast(u12, adj_off)) |imm| blk: {
3491 break :blk Instruction.Offset.imm(imm);3612 break :blk Instruction.Offset.imm(imm);
3492 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);3613 } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3493 const str = switch (abi_size) {3614 const str = switch (abi_size) {
3494 1 => Instruction.strb,3615 1 => Instruction.strb,
3495 4 => Instruction.str,3616 4 => Instruction.str,
...@@ -3504,26 +3625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3504,26 +3625,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3504 2 => {3625 2 => {
3505 const offset = if (adj_off <= math.maxInt(u8)) blk: {3626 const offset = if (adj_off <= math.maxInt(u8)) blk: {
3506 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));3627 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3507 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));3628 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
35083629
3509 writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{3630 writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{
3510 .offset = offset,3631 .offset = offset,
3511 .positive = false,3632 .positive = false,
3512 }).toU32());3633 }).toU32());
3513 },3634 },
3514 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),3635 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
3515 }3636 }
3516 },3637 },
3517 .memory => |vaddr| {3638 .memory => |vaddr| {
3518 _ = vaddr;3639 _ = vaddr;
3519 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3640 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3520 },3641 },
3521 .stack_offset => |off| {3642 .stack_offset => |off| {
3522 if (stack_offset == off)3643 if (stack_offset == off)
3523 return; // Copy stack variable to itself; nothing to do.3644 return; // Copy stack variable to itself; nothing to do.
35243645
3525 const reg = try self.copyToTmpRegister(src, ty, mcv);3646 const reg = try self.copyToTmpRegister(ty, mcv);
3526 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3647 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3527 },3648 },
3528 },3649 },
3529 .x86_64 => switch (mcv) {3650 .x86_64 => switch (mcv) {
...@@ -3536,34 +3657,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3536,34 +3657,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3536 return; // The already existing value will do just fine.3657 return; // The already existing value will do just fine.
3537 // TODO Upgrade this to a memset call when we have that available.3658 // TODO Upgrade this to a memset call when we have that available.
3538 switch (ty.abiSize(self.target.*)) {3659 switch (ty.abiSize(self.target.*)) {
3539 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3660 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3540 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3661 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3541 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3662 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3542 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3663 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3543 else => return self.fail(src, "TODO implement memset", .{}),3664 else => return self.fail("TODO implement memset", .{}),
3544 }3665 }
3545 },3666 },
3546 .compare_flags_unsigned => |op| {3667 .compare_flags_unsigned => |op| {
3547 _ = op;3668 _ = op;
3548 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3669 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3549 },3670 },
3550 .compare_flags_signed => |op| {3671 .compare_flags_signed => |op| {
3551 _ = op;3672 _ = op;
3552 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3673 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3553 },3674 },
3554 .immediate => |x_big| {3675 .immediate => |x_big| {
3555 const abi_size = ty.abiSize(self.target.*);3676 const abi_size = ty.abiSize(self.target.*);
3556 const adj_off = stack_offset + abi_size;3677 const adj_off = stack_offset + abi_size;
3557 if (adj_off > 128) {3678 if (adj_off > 128) {
3558 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});3679 return self.fail("TODO implement set stack variable with large stack offset", .{});
3559 }3680 }
3560 try self.code.ensureCapacity(self.code.items.len + 8);3681 try self.code.ensureCapacity(self.code.items.len + 8);
3561 switch (abi_size) {3682 switch (abi_size) {
3562 1 => {3683 1 => {
3563 return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});3684 return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{});
3564 },3685 },
3565 2 => {3686 2 => {
3566 return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});3687 return self.fail("TODO implement set abi_size=2 stack variable with immediate", .{});
3567 },3688 },
3568 4 => {3689 4 => {
3569 const x = @intCast(u32, x_big);3690 const x = @intCast(u32, x_big);
...@@ -3596,22 +3717,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3596,22 +3717,22 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3596 self.code.appendSliceAssumeCapacity(buf[0..4]);3717 self.code.appendSliceAssumeCapacity(buf[0..4]);
3597 },3718 },
3598 else => {3719 else => {
3599 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});3720 return self.fail("TODO implement set abi_size=large stack variable with immediate", .{});
3600 },3721 },
3601 }3722 }
3602 },3723 },
3603 .embedded_in_code => {3724 .embedded_in_code => {
3604 // TODO this and `.stack_offset` below need to get improved to support types greater than3725 // TODO this and `.stack_offset` below need to get improved to support types greater than
3605 // register size, and do general memcpy3726 // register size, and do general memcpy
3606 const reg = try self.copyToTmpRegister(src, ty, mcv);3727 const reg = try self.copyToTmpRegister(ty, mcv);
3607 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3728 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3608 },3729 },
3609 .register => |reg| {3730 .register => |reg| {
3610 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);3731 try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89);
3611 },3732 },
3612 .memory => |vaddr| {3733 .memory => |vaddr| {
3613 _ = vaddr;3734 _ = vaddr;
3614 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3735 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3615 },3736 },
3616 .stack_offset => |off| {3737 .stack_offset => |off| {
3617 // TODO this and `.embedded_in_code` above need to get improved to support types greater than3738 // TODO this and `.embedded_in_code` above need to get improved to support types greater than
...@@ -3620,8 +3741,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3620,8 +3741,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3620 if (stack_offset == off)3741 if (stack_offset == off)
3621 return; // Copy stack variable to itself; nothing to do.3742 return; // Copy stack variable to itself; nothing to do.
36223743
3623 const reg = try self.copyToTmpRegister(src, ty, mcv);3744 const reg = try self.copyToTmpRegister(ty, mcv);
3624 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3745 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3625 },3746 },
3626 },3747 },
3627 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {3748 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {
...@@ -3634,28 +3755,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3634,28 +3755,28 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3634 return; // The already existing value will do just fine.3755 return; // The already existing value will do just fine.
3635 // TODO Upgrade this to a memset call when we have that available.3756 // TODO Upgrade this to a memset call when we have that available.
3636 switch (ty.abiSize(self.target.*)) {3757 switch (ty.abiSize(self.target.*)) {
3637 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),3758 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
3638 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),3759 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3639 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3760 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3640 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3761 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3641 else => return self.fail(src, "TODO implement memset", .{}),3762 else => return self.fail("TODO implement memset", .{}),
3642 }3763 }
3643 },3764 },
3644 .compare_flags_unsigned => |op| {3765 .compare_flags_unsigned => |op| {
3645 _ = op;3766 _ = op;
3646 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});3767 return self.fail("TODO implement set stack variable with compare flags value (unsigned)", .{});
3647 },3768 },
3648 .compare_flags_signed => |op| {3769 .compare_flags_signed => |op| {
3649 _ = op;3770 _ = op;
3650 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});3771 return self.fail("TODO implement set stack variable with compare flags value (signed)", .{});
3651 },3772 },
3652 .immediate => {3773 .immediate => {
3653 const reg = try self.copyToTmpRegister(src, ty, mcv);3774 const reg = try self.copyToTmpRegister(ty, mcv);
3654 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3775 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3655 },3776 },
3656 .embedded_in_code => |code_offset| {3777 .embedded_in_code => |code_offset| {
3657 _ = code_offset;3778 _ = code_offset;
3658 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});3779 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
3659 },3780 },
3660 .register => |reg| {3781 .register => |reg| {
3661 const abi_size = ty.abiSize(self.target.*);3782 const abi_size = ty.abiSize(self.target.*);
...@@ -3666,7 +3787,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3666,7 +3787,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3666 const offset = if (math.cast(i9, adj_off)) |imm|3787 const offset = if (math.cast(i9, adj_off)) |imm|
3667 Instruction.LoadStoreOffset.imm_post_index(-imm)3788 Instruction.LoadStoreOffset.imm_post_index(-imm)
3668 else |_|3789 else |_|
3669 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));3790 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
3670 const rn: Register = switch (arch) {3791 const rn: Register = switch (arch) {
3671 .aarch64, .aarch64_be => .x29,3792 .aarch64, .aarch64_be => .x29,
3672 .aarch64_32 => .w29,3793 .aarch64_32 => .w29,
...@@ -3683,26 +3804,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3683,26 +3804,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3683 .offset = offset,3804 .offset = offset,
3684 }).toU32());3805 }).toU32());
3685 },3806 },
3686 else => return self.fail(src, "TODO implement storing other types abi_size={}", .{abi_size}),3807 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
3687 }3808 }
3688 },3809 },
3689 .memory => |vaddr| {3810 .memory => |vaddr| {
3690 _ = vaddr;3811 _ = vaddr;
3691 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});3812 return self.fail("TODO implement set stack variable from memory vaddr", .{});
3692 },3813 },
3693 .stack_offset => |off| {3814 .stack_offset => |off| {
3694 if (stack_offset == off)3815 if (stack_offset == off)
3695 return; // Copy stack variable to itself; nothing to do.3816 return; // Copy stack variable to itself; nothing to do.
36963817
3697 const reg = try self.copyToTmpRegister(src, ty, mcv);3818 const reg = try self.copyToTmpRegister(ty, mcv);
3698 return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg });3819 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
3699 },3820 },
3700 },3821 },
3701 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),3822 else => return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}),
3702 }3823 }
3703 }3824 }
37043825
3705 fn genSetReg(self: *Self, src: LazySrcLoc, ty: Type, reg: Register, mcv: MCValue) InnerError!void {3826 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
3706 switch (arch) {3827 switch (arch) {
3707 .arm, .armeb => switch (mcv) {3828 .arm, .armeb => switch (mcv) {
3708 .dead => unreachable,3829 .dead => unreachable,
...@@ -3713,7 +3834,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3713,7 +3834,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3713 if (!self.wantSafety())3834 if (!self.wantSafety())
3714 return; // The already existing value will do just fine.3835 return; // The already existing value will do just fine.
3715 // Write the debug undefined value.3836 // Write the debug undefined value.
3716 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa });3837 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa });
3717 },3838 },
3718 .compare_flags_unsigned,3839 .compare_flags_unsigned,
3719 .compare_flags_signed,3840 .compare_flags_signed,
...@@ -3732,7 +3853,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3732,7 +3853,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3732 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());3853 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32());
3733 },3854 },
3734 .immediate => |x| {3855 .immediate => |x| {
3735 if (x > math.maxInt(u32)) return self.fail(src, "ARM registers are 32-bit wide", .{});3856 if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{});
37363857
3737 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {3858 if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| {
3738 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());3859 writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32());
...@@ -3778,7 +3899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3778,7 +3899,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3778 .memory => |addr| {3899 .memory => |addr| {
3779 // The value is in memory at a hard-coded address.3900 // The value is in memory at a hard-coded address.
3780 // If the type is a pointer, it means the pointer address is at this memory location.3901 // If the type is a pointer, it means the pointer address is at this memory location.
3781 try self.genSetReg(src, ty, reg, .{ .immediate = addr });3902 try self.genSetReg(ty, reg, .{ .immediate = addr });
3782 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());3903 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32());
3783 },3904 },
3784 .stack_offset => |unadjusted_off| {3905 .stack_offset => |unadjusted_off| {
...@@ -3790,7 +3911,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3790,7 +3911,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3790 1, 4 => {3911 1, 4 => {
3791 const offset = if (adj_off <= math.maxInt(u12)) blk: {3912 const offset = if (adj_off <= math.maxInt(u12)) blk: {
3792 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));3913 break :blk Instruction.Offset.imm(@intCast(u12, adj_off));
3793 } else Instruction.Offset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);3914 } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0);
3794 const ldr = switch (abi_size) {3915 const ldr = switch (abi_size) {
3795 1 => Instruction.ldrb,3916 1 => Instruction.ldrb,
3796 4 => Instruction.ldr,3917 4 => Instruction.ldr,
...@@ -3805,17 +3926,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3805,17 +3926,17 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3805 2 => {3926 2 => {
3806 const offset = if (adj_off <= math.maxInt(u8)) blk: {3927 const offset = if (adj_off <= math.maxInt(u8)) blk: {
3807 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));3928 break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off));
3808 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u32), MCValue{ .immediate = adj_off }));3929 } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }));
38093930
3810 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{3931 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{
3811 .offset = offset,3932 .offset = offset,
3812 .positive = false,3933 .positive = false,
3813 }).toU32());3934 }).toU32());
3814 },3935 },
3815 else => return self.fail(src, "TODO a type of size {} is not allowed in a register", .{abi_size}),3936 else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}),
3816 }3937 }
3817 },3938 },
3818 else => return self.fail(src, "TODO implement getSetReg for arm {}", .{mcv}),3939 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
3819 },3940 },
3820 .aarch64 => switch (mcv) {3941 .aarch64 => switch (mcv) {
3821 .dead => unreachable,3942 .dead => unreachable,
...@@ -3827,8 +3948,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3827,8 +3948,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3827 return; // The already existing value will do just fine.3948 return; // The already existing value will do just fine.
3828 // Write the debug undefined value.3949 // Write the debug undefined value.
3829 switch (reg.size()) {3950 switch (reg.size()) {
3830 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),3951 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
3831 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3952 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3832 else => unreachable, // unexpected register size3953 else => unreachable, // unexpected register size
3833 }3954 }
3834 },3955 },
...@@ -3876,7 +3997,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3876,7 +3997,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3876 .size = 4,3997 .size = 4,
3877 });3998 });
3878 } else {3999 } else {
3879 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});4000 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
3880 }4001 }
3881 mem.writeIntLittle(4002 mem.writeIntLittle(
3882 u32,4003 u32,
...@@ -3893,7 +4014,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3893,7 +4014,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3893 } else {4014 } else {
3894 // The value is in memory at a hard-coded address.4015 // The value is in memory at a hard-coded address.
3895 // If the type is a pointer, it means the pointer address is at this memory location.4016 // If the type is a pointer, it means the pointer address is at this memory location.
3896 try self.genSetReg(src, Type.initTag(.usize), reg, .{ .immediate = addr });4017 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
3897 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());4018 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
3898 }4019 }
3899 },4020 },
...@@ -3911,7 +4032,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3911,7 +4032,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3911 const offset = if (math.cast(i9, adj_off)) |imm|4032 const offset = if (math.cast(i9, adj_off)) |imm|
3912 Instruction.LoadStoreOffset.imm_post_index(-imm)4033 Instruction.LoadStoreOffset.imm_post_index(-imm)
3913 else |_|4034 else |_|
3914 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(src, Type.initTag(.u64), MCValue{ .immediate = adj_off }));4035 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
39154036
3916 switch (abi_size) {4037 switch (abi_size) {
3917 1, 2 => {4038 1, 2 => {
...@@ -3931,10 +4052,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3931,10 +4052,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3931 .offset = offset,4052 .offset = offset,
3932 } }).toU32());4053 } }).toU32());
3933 },4054 },
3934 else => return self.fail(src, "TODO implement genSetReg other types abi_size={}", .{abi_size}),4055 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
3935 }4056 }
3936 },4057 },
3937 else => return self.fail(src, "TODO implement genSetReg for aarch64 {}", .{mcv}),4058 else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}),
3938 },4059 },
3939 .riscv64 => switch (mcv) {4060 .riscv64 => switch (mcv) {
3940 .dead => unreachable,4061 .dead => unreachable,
...@@ -3945,7 +4066,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3945,7 +4066,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3945 if (!self.wantSafety())4066 if (!self.wantSafety())
3946 return; // The already existing value will do just fine.4067 return; // The already existing value will do just fine.
3947 // Write the debug undefined value.4068 // Write the debug undefined value.
3948 return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });4069 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
3949 },4070 },
3950 .immediate => |unsigned_x| {4071 .immediate => |unsigned_x| {
3951 const x = @bitCast(i64, unsigned_x);4072 const x = @bitCast(i64, unsigned_x);
...@@ -3965,19 +4086,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3965,19 +4086,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3965 }4086 }
3966 // li rd, immediate4087 // li rd, immediate
3967 // "Myriad sequences"4088 // "Myriad sequences"
3968 return self.fail(src, "TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf4089 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
3969 },4090 },
3970 .memory => |addr| {4091 .memory => |addr| {
3971 // The value is in memory at a hard-coded address.4092 // The value is in memory at a hard-coded address.
3972 // If the type is a pointer, it means the pointer address is at this memory location.4093 // If the type is a pointer, it means the pointer address is at this memory location.
3973 try self.genSetReg(src, ty, reg, .{ .immediate = addr });4094 try self.genSetReg(ty, reg, .{ .immediate = addr });
39744095
3975 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());4096 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
3976 // LOAD imm=[i12 offset = 0], rs1 =4097 // LOAD imm=[i12 offset = 0], rs1 =
39774098
3978 // return self.fail("TODO implement genSetReg memory for riscv64");4099 // return self.fail("TODO implement genSetReg memory for riscv64");
3979 },4100 },
3980 else => return self.fail(src, "TODO implement getSetReg for riscv64 {}", .{mcv}),4101 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
3981 },4102 },
3982 .x86_64 => switch (mcv) {4103 .x86_64 => switch (mcv) {
3983 .dead => unreachable,4104 .dead => unreachable,
...@@ -3989,10 +4110,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3989,10 +4110,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3989 return; // The already existing value will do just fine.4110 return; // The already existing value will do just fine.
3990 // Write the debug undefined value.4111 // Write the debug undefined value.
3991 switch (reg.size()) {4112 switch (reg.size()) {
3992 8 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaa }),4113 8 => return self.genSetReg(ty, reg, .{ .immediate = 0xaa }),
3993 16 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaa }),4114 16 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaa }),
3994 32 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaa }),4115 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
3995 64 => return self.genSetReg(src, ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),4116 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3996 else => unreachable,4117 else => unreachable,
3997 }4118 }
3998 },4119 },
...@@ -4019,7 +4140,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4019,7 +4140,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4019 },4140 },
4020 .compare_flags_signed => |op| {4141 .compare_flags_signed => |op| {
4021 _ = op;4142 _ = op;
4022 return self.fail(src, "TODO set register with compare flags value (signed)", .{});4143 return self.fail("TODO set register with compare flags value (signed)", .{});
4023 },4144 },
4024 .immediate => |x| {4145 .immediate => |x| {
4025 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit4146 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
...@@ -4152,7 +4273,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4152,7 +4273,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4152 .size = 4,4273 .size = 4,
4153 });4274 });
4154 } else {4275 } else {
4155 return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{});4276 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
4156 }4277 }
41574278
4158 // MOV reg, [reg]4279 // MOV reg, [reg]
...@@ -4208,7 +4329,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4208,7 +4329,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4208 assert(id3 != 4 and id3 != 5);4329 assert(id3 != 4 and id3 != 5);
42094330
4210 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.4331 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
4211 try self.genSetReg(src, ty, reg, MCValue{ .immediate = x });4332 try self.genSetReg(ty, reg, MCValue{ .immediate = x });
42124333
4213 // Now, the register contains the address of the value to load into it4334 // Now, the register contains the address of the value to load into it
4214 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.4335 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
...@@ -4231,7 +4352,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4231,7 +4352,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4231 const abi_size = ty.abiSize(self.target.*);4352 const abi_size = ty.abiSize(self.target.*);
4232 const off = unadjusted_off + abi_size;4353 const off = unadjusted_off + abi_size;
4233 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {4354 if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) {
4234 return self.fail(src, "stack offset too large", .{});4355 return self.fail("stack offset too large", .{});
4235 }4356 }
4236 const ioff = -@intCast(i32, off);4357 const ioff = -@intCast(i32, off);
4237 const encoder = try X8664Encoder.init(self.code, 3);4358 const encoder = try X8664Encoder.init(self.code, 3);
...@@ -4251,21 +4372,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4251,21 +4372,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4251 }4372 }
4252 },4373 },
4253 },4374 },
4254 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),4375 else => return self.fail("TODO implement getSetReg for {}", .{self.target.cpu.arch}),
4255 }4376 }
4256 }4377 }
42574378
4258 fn genPtrToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {4379 fn genPtrToInt(self: *Self, inst: Air.Inst.Index) !MCValue {
4259 // no-op4380 const inst_datas = self.air.instructions.items(.data);
4260 return self.resolveInst(inst.operand);4381 return self.resolveInst(inst_datas[inst].un_op);
4261 }4382 }
42624383
4263 fn genBitCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {4384 fn genBitCast(self: *Self, inst: Air.Inst.Index) !MCValue {
4264 const operand = try self.resolveInst(inst.operand);4385 const inst_datas = self.air.instructions.items(.data);
4265 return operand;4386 return self.resolveInst(inst_datas[inst].ty_op.operand);
4266 }4387 }
42674388
4268 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {4389 fn resolveInst(self: *Self, inst: Air.Inst.Index) !MCValue {
4269 // If the type has no codegen bits, no need to store it.4390 // If the type has no codegen bits, no need to store it.
4270 if (!inst.ty.hasCodeGenBits())4391 if (!inst.ty.hasCodeGenBits())
4271 return MCValue.none;4392 return MCValue.none;
...@@ -4283,7 +4404,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4283,7 +4404,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4283 return self.getResolvedInstValue(inst);4404 return self.getResolvedInstValue(inst);
4284 }4405 }
42854406
4286 fn getResolvedInstValue(self: *Self, inst: *ir.Inst) MCValue {4407 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
4287 // Treat each stack item as a "layer" on top of the previous one.4408 // Treat each stack item as a "layer" on top of the previous one.
4288 var i: usize = self.branch_stack.items.len;4409 var i: usize = self.branch_stack.items.len;
4289 while (true) {4410 while (true) {
...@@ -4300,7 +4421,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4300,7 +4421,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4300 /// A potential opportunity for future optimization here would be keeping track4421 /// A potential opportunity for future optimization here would be keeping track
4301 /// of the fact that the instruction is available both as an immediate4422 /// of the fact that the instruction is available both as an immediate
4302 /// and as a register.4423 /// and as a register.
4303 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {4424 fn limitImmediateType(self: *Self, inst: Air.Inst.Index, comptime T: type) !MCValue {
4304 const mcv = try self.resolveInst(inst);4425 const mcv = try self.resolveInst(inst);
4305 const ti = @typeInfo(T).Int;4426 const ti = @typeInfo(T).Int;
4306 switch (mcv) {4427 switch (mcv) {
...@@ -4308,7 +4429,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4308,7 +4429,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4308 // This immediate is unsigned.4429 // This immediate is unsigned.
4309 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));4430 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
4310 if (imm >= math.maxInt(U)) {4431 if (imm >= math.maxInt(U)) {
4311 return MCValue{ .register = try self.copyToTmpRegister(inst.src, Type.initTag(.usize), mcv) };4432 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
4312 }4433 }
4313 },4434 },
4314 else => {},4435 else => {},
...@@ -4334,7 +4455,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4334,7 +4455,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4334 _ = slice_len;4455 _ = slice_len;
4335 _ = ptr_imm;4456 _ = ptr_imm;
4336 // We need more general support for const data being stored in memory to make this work.4457 // We need more general support for const data being stored in memory to make this work.
4337 return self.fail(src, "TODO codegen for const slices", .{});4458 return self.fail("TODO codegen for const slices", .{});
4338 },4459 },
4339 else => {4460 else => {
4340 if (typed_value.val.castTag(.decl_ref)) |payload| {4461 if (typed_value.val.castTag(.decl_ref)) |payload| {
...@@ -4360,19 +4481,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4360,19 +4481,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4360 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;4481 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
4361 return MCValue{ .memory = got_addr };4482 return MCValue{ .memory = got_addr };
4362 } else {4483 } else {
4363 return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{});4484 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
4364 }4485 }
4365 }4486 }
4366 if (typed_value.val.tag() == .int_u64) {4487 if (typed_value.val.tag() == .int_u64) {
4367 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };4488 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4368 }4489 }
4369 return self.fail(src, "TODO codegen more kinds of const pointers", .{});4490 return self.fail("TODO codegen more kinds of const pointers", .{});
4370 },4491 },
4371 },4492 },
4372 .Int => {4493 .Int => {
4373 const info = typed_value.ty.intInfo(self.target.*);4494 const info = typed_value.ty.intInfo(self.target.*);
4374 if (info.bits > ptr_bits or info.signedness == .signed) {4495 if (info.bits > ptr_bits or info.signedness == .signed) {
4375 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});4496 return self.fail("TODO const int bigger than ptr and signed int", .{});
4376 }4497 }
4377 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };4498 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
4378 },4499 },
...@@ -4394,9 +4515,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4394,9 +4515,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4394 } else if (typed_value.ty.abiSize(self.target.*) == 1) {4515 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
4395 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };4516 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
4396 }4517 }
4397 return self.fail(src, "TODO non pointer optionals", .{});4518 return self.fail("TODO non pointer optionals", .{});
4398 },4519 },
4399 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),4520 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
4400 }4521 }
4401 }4522 }
44024523
...@@ -4413,7 +4534,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4413,7 +4534,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4413 };4534 };
44144535
4415 /// Caller must call `CallMCValues.deinit`.4536 /// Caller must call `CallMCValues.deinit`.
4416 fn resolveCallingConventionValues(self: *Self, src: LazySrcLoc, fn_ty: Type) !CallMCValues {4537 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4417 const cc = fn_ty.fnCallingConvention();4538 const cc = fn_ty.fnCallingConvention();
4418 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());4539 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
4419 defer self.gpa.free(param_types);4540 defer self.gpa.free(param_types);
...@@ -4482,7 +4603,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4482,7 +4603,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4482 result.stack_byte_count = next_stack_offset;4603 result.stack_byte_count = next_stack_offset;
4483 result.stack_align = 16;4604 result.stack_align = 16;
4484 },4605 },
4485 else => return self.fail(src, "TODO implement function parameters for {} on x86_64", .{cc}),4606 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),
4486 }4607 }
4487 },4608 },
4488 .arm, .armeb => {4609 .arm, .armeb => {
...@@ -4509,10 +4630,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4509,10 +4630,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4509 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };4630 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
4510 ncrn += 1;4631 ncrn += 1;
4511 } else {4632 } else {
4512 return self.fail(src, "TODO MCValues with multiple registers", .{});4633 return self.fail("TODO MCValues with multiple registers", .{});
4513 }4634 }
4514 } else if (ncrn < 4 and nsaa == 0) {4635 } else if (ncrn < 4 and nsaa == 0) {
4515 return self.fail(src, "TODO MCValues split between registers and stack", .{});4636 return self.fail("TODO MCValues split between registers and stack", .{});
4516 } else {4637 } else {
4517 ncrn = 4;4638 ncrn = 4;
4518 if (ty.abiAlignment(self.target.*) == 8)4639 if (ty.abiAlignment(self.target.*) == 8)
...@@ -4526,7 +4647,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4526,7 +4647,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4526 result.stack_byte_count = nsaa;4647 result.stack_byte_count = nsaa;
4527 result.stack_align = 4;4648 result.stack_align = 4;
4528 },4649 },
4529 else => return self.fail(src, "TODO implement function parameters for {} on arm", .{cc}),4650 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
4530 }4651 }
4531 },4652 },
4532 .aarch64 => {4653 .aarch64 => {
...@@ -4557,10 +4678,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4557,10 +4678,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4557 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };4678 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
4558 ncrn += 1;4679 ncrn += 1;
4559 } else {4680 } else {
4560 return self.fail(src, "TODO MCValues with multiple registers", .{});4681 return self.fail("TODO MCValues with multiple registers", .{});
4561 }4682 }
4562 } else if (ncrn < 8 and nsaa == 0) {4683 } else if (ncrn < 8 and nsaa == 0) {
4563 return self.fail(src, "TODO MCValues split between registers and stack", .{});4684 return self.fail("TODO MCValues split between registers and stack", .{});
4564 } else {4685 } else {
4565 ncrn = 8;4686 ncrn = 8;
4566 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided4687 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
...@@ -4579,11 +4700,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4579,11 +4700,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4579 result.stack_byte_count = nsaa;4700 result.stack_byte_count = nsaa;
4580 result.stack_align = 16;4701 result.stack_align = 16;
4581 },4702 },
4582 else => return self.fail(src, "TODO implement function parameters for {} on aarch64", .{cc}),4703 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
4583 }4704 }
4584 },4705 },
4585 else => if (param_types.len != 0)4706 else => if (param_types.len != 0)
4586 return self.fail(src, "TODO implement codegen parameters for {}", .{self.target.cpu.arch}),4707 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
4587 }4708 }
45884709
4589 if (ret_ty.zigTypeTag() == .NoReturn) {4710 if (ret_ty.zigTypeTag() == .NoReturn) {
...@@ -4598,7 +4719,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4598,7 +4719,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4598 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);4719 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
4599 result.return_value = .{ .register = aliased_reg };4720 result.return_value = .{ .register = aliased_reg };
4600 },4721 },
4601 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4722 else => return self.fail("TODO implement function return values for {}", .{cc}),
4602 },4723 },
4603 .arm, .armeb => switch (cc) {4724 .arm, .armeb => switch (cc) {
4604 .Naked => unreachable,4725 .Naked => unreachable,
...@@ -4607,10 +4728,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4607,10 +4728,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4607 if (ret_ty_size <= 4) {4728 if (ret_ty_size <= 4) {
4608 result.return_value = .{ .register = c_abi_int_return_regs[0] };4729 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4609 } else {4730 } else {
4610 return self.fail(src, "TODO support more return types for ARM backend", .{});4731 return self.fail("TODO support more return types for ARM backend", .{});
4611 }4732 }
4612 },4733 },
4613 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4734 else => return self.fail("TODO implement function return values for {}", .{cc}),
4614 },4735 },
4615 .aarch64 => switch (cc) {4736 .aarch64 => switch (cc) {
4616 .Naked => unreachable,4737 .Naked => unreachable,
...@@ -4619,12 +4740,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -4619,12 +4740,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4619 if (ret_ty_size <= 8) {4740 if (ret_ty_size <= 8) {
4620 result.return_value = .{ .register = c_abi_int_return_regs[0] };4741 result.return_value = .{ .register = c_abi_int_return_regs[0] };
4621 } else {4742 } else {
4622 return self.fail(src, "TODO support more return types for ARM backend", .{});4743 return self.fail("TODO support more return types for ARM backend", .{});
4623 }4744 }
4624 },4745 },
4625 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),4746 else => return self.fail("TODO implement function return values for {}", .{cc}),
4626 },4747 },
4627 else => return self.fail(src, "TODO implement codegen return values for {}", .{self.target.cpu.arch}),4748 else => return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch}),
4628 }4749 }
4629 return result;4750 return result;
4630 }4751 }
src/register_manager.zig+5-6
...@@ -147,14 +147,14 @@ pub fn RegisterManager(...@@ -147,14 +147,14 @@ pub fn RegisterManager(
147 self.markRegUsed(reg);147 self.markRegUsed(reg);
148 } else {148 } else {
149 const spilled_inst = self.registers[index].?;149 const spilled_inst = self.registers[index].?;
150 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);150 try self.getFunction().spillInstruction(reg, spilled_inst);
151 }151 }
152 self.registers[index] = inst;152 self.registers[index] = inst;
153 } else {153 } else {
154 // Don't track the register154 // Don't track the register
155 if (!self.isRegFree(reg)) {155 if (!self.isRegFree(reg)) {
156 const spilled_inst = self.registers[index].?;156 const spilled_inst = self.registers[index].?;
157 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);157 try self.getFunction().spillInstruction(reg, spilled_inst);
158 self.freeReg(reg);158 self.freeReg(reg);
159 }159 }
160 }160 }
...@@ -184,7 +184,7 @@ pub fn RegisterManager(...@@ -184,7 +184,7 @@ pub fn RegisterManager(
184 // stack allocation.184 // stack allocation.
185 const spilled_inst = self.registers[index].?;185 const spilled_inst = self.registers[index].?;
186 self.registers[index] = tracked_inst;186 self.registers[index] = tracked_inst;
187 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);187 try self.getFunction().spillInstruction(reg, spilled_inst);
188 } else {188 } else {
189 self.getRegAssumeFree(reg, tracked_inst);189 self.getRegAssumeFree(reg, tracked_inst);
190 }190 }
...@@ -193,7 +193,7 @@ pub fn RegisterManager(...@@ -193,7 +193,7 @@ pub fn RegisterManager(
193 // Move the instruction that was previously there to a193 // Move the instruction that was previously there to a
194 // stack allocation.194 // stack allocation.
195 const spilled_inst = self.registers[index].?;195 const spilled_inst = self.registers[index].?;
196 try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst);196 try self.getFunction().spillInstruction(reg, spilled_inst);
197 self.freeReg(reg);197 self.freeReg(reg);
198 }198 }
199 }199 }
...@@ -264,8 +264,7 @@ fn MockFunction(comptime Register: type) type {...@@ -264,8 +264,7 @@ fn MockFunction(comptime Register: type) type {
264 self.spilled.deinit(self.allocator);264 self.spilled.deinit(self.allocator);
265 }265 }
266266
267 pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: Register, inst: *ir.Inst) !void {267 pub fn spillInstruction(self: *Self, reg: Register, inst: *ir.Inst) !void {
268 _ = src;
269 _ = inst;268 _ = inst;
270 try self.spilled.append(self.allocator, reg);269 try self.spilled.append(self.allocator, reg);
271 }270 }