| ... | ... | @@ -83,6 +83,8 @@ max_end_stack: u32 = 0, |
| 83 | 83 | /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. |
| 84 | 84 | next_stack_offset: u32 = 0, |
| 85 | 85 | |
| 86 | saved_regs_stack_space: u32 = 0, |
| 87 | |
| 86 | 88 | /// Debug field, used to find bugs in the compiler. |
| 87 | 89 | air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 88 | 90 | |
| ... | ... | @@ -123,10 +125,12 @@ const MCValue = union(enum) { |
| 123 | 125 | /// The value is in the compare flags assuming a signed operation, |
| 124 | 126 | /// with this operator applied on top of it. |
| 125 | 127 | compare_flags_signed: math.CompareOperator, |
| 128 | /// The value is a function argument passed via the stack. |
| 129 | stack_argument_offset: u32, |
| 126 | 130 | |
| 127 | 131 | fn isMemory(mcv: MCValue) bool { |
| 128 | 132 | return switch (mcv) { |
| 129 | | .embedded_in_code, .memory, .stack_offset => true, |
| 133 | .embedded_in_code, .memory, .stack_offset, .stack_argument_offset => true, |
| 130 | 134 | else => false, |
| 131 | 135 | }; |
| 132 | 136 | } |
| ... | ... | @@ -152,6 +156,7 @@ const MCValue = union(enum) { |
| 152 | 156 | .ptr_stack_offset, |
| 153 | 157 | .ptr_embedded_in_code, |
| 154 | 158 | .undef, |
| 159 | .stack_argument_offset, |
| 155 | 160 | => false, |
| 156 | 161 | |
| 157 | 162 | .register, |
| ... | ... | @@ -302,6 +307,7 @@ pub fn generate( |
| 302 | 307 | .prev_di_pc = 0, |
| 303 | 308 | .prev_di_line = module_fn.lbrace_line, |
| 304 | 309 | .prev_di_column = module_fn.lbrace_column, |
| 310 | .prologue_stack_space = call_info.stack_byte_count + function.saved_regs_stack_space, |
| 305 | 311 | }; |
| 306 | 312 | defer emit.deinit(); |
| 307 | 313 | |
| ... | ... | @@ -387,11 +393,14 @@ fn gen(self: *Self) !void { |
| 387 | 393 | .r11 = true, // fp |
| 388 | 394 | .r14 = true, // lr |
| 389 | 395 | }; |
| 396 | self.saved_regs_stack_space = 8; |
| 390 | 397 | inline for (callee_preserved_regs) |reg| { |
| 391 | 398 | if (self.register_manager.isRegAllocated(reg)) { |
| 392 | 399 | @field(saved_regs, @tagName(reg)) = true; |
| 400 | self.saved_regs_stack_space += 4; |
| 393 | 401 | } |
| 394 | 402 | } |
| 403 | |
| 395 | 404 | self.mir_instructions.set(push_reloc, .{ |
| 396 | 405 | .tag = .push, |
| 397 | 406 | .cond = .al, |
| ... | ... | @@ -399,9 +408,10 @@ fn gen(self: *Self) !void { |
| 399 | 408 | }); |
| 400 | 409 | |
| 401 | 410 | // Backpatch stack offset |
| 402 | | const stack_end = self.max_end_stack; |
| 403 | | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 404 | | if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| { |
| 411 | const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; |
| 412 | const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); |
| 413 | const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; |
| 414 | if (Instruction.Operand.fromU32(stack_size)) |op| { |
| 405 | 415 | self.mir_instructions.set(sub_reloc, .{ |
| 406 | 416 | .tag = .sub, |
| 407 | 417 | .cond = .al, |
| ... | ... | @@ -1256,6 +1266,9 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1256 | 1266 | .stack_offset => { |
| 1257 | 1267 | return self.fail("TODO implement loading from MCValue.stack_offset", .{}); |
| 1258 | 1268 | }, |
| 1269 | .stack_argument_offset => { |
| 1270 | return self.fail("TODO implement loading from MCValue.stack_argument_offset", .{}); |
| 1271 | }, |
| 1259 | 1272 | } |
| 1260 | 1273 | } |
| 1261 | 1274 | |
| ... | ... | @@ -1297,6 +1310,7 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 1297 | 1310 | .dead => unreachable, |
| 1298 | 1311 | .compare_flags_unsigned => unreachable, |
| 1299 | 1312 | .compare_flags_signed => unreachable, |
| 1313 | .stack_argument_offset => unreachable, |
| 1300 | 1314 | .immediate => |imm| { |
| 1301 | 1315 | try self.setRegOrMem(elem_ty, .{ .memory = imm }, value); |
| 1302 | 1316 | }, |
| ... | ... | @@ -1367,6 +1381,7 @@ fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { |
| 1367 | 1381 | }, |
| 1368 | 1382 | .register => true, |
| 1369 | 1383 | .stack_offset, |
| 1384 | .stack_argument_offset, |
| 1370 | 1385 | .embedded_in_code, |
| 1371 | 1386 | .memory, |
| 1372 | 1387 | => true, |
| ... | ... | @@ -1533,6 +1548,7 @@ fn genArmBinOpCode( |
| 1533 | 1548 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, |
| 1534 | 1549 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), |
| 1535 | 1550 | .stack_offset, |
| 1551 | .stack_argument_offset, |
| 1536 | 1552 | .embedded_in_code, |
| 1537 | 1553 | .memory, |
| 1538 | 1554 | => unreachable, |
| ... | ... | @@ -1749,6 +1765,7 @@ fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1749 | 1765 | .none => {}, |
| 1750 | 1766 | } |
| 1751 | 1767 | }, |
| 1768 | .stack_argument_offset => return self.fail("TODO genArgDbgInfo for stack_argument_offset", .{}), |
| 1752 | 1769 | else => {}, |
| 1753 | 1770 | } |
| 1754 | 1771 | } |
| ... | ... | @@ -1814,6 +1831,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1814 | 1831 | var info = try self.resolveCallingConventionValues(fn_ty); |
| 1815 | 1832 | defer info.deinit(self); |
| 1816 | 1833 | |
| 1834 | // Make space for the arguments passed via the stack |
| 1835 | self.max_end_stack += info.stack_byte_count; |
| 1836 | |
| 1817 | 1837 | // Due to incremental compilation, how function calls are generated depends |
| 1818 | 1838 | // on linking. |
| 1819 | 1839 | if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { |
| ... | ... | @@ -1836,9 +1856,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1836 | 1856 | try self.register_manager.getReg(reg, null); |
| 1837 | 1857 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| 1838 | 1858 | }, |
| 1839 | | .stack_offset => { |
| 1840 | | return self.fail("TODO implement calling with parameters in memory", .{}); |
| 1841 | | }, |
| 1859 | .stack_offset => unreachable, |
| 1860 | .stack_argument_offset => |offset| try self.genSetStackArgument( |
| 1861 | arg_ty, |
| 1862 | info.stack_byte_count - offset, |
| 1863 | arg_mcv, |
| 1864 | ), |
| 1842 | 1865 | .ptr_stack_offset => { |
| 1843 | 1866 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 1844 | 1867 | }, |
| ... | ... | @@ -2616,16 +2639,26 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2616 | 2639 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 2617 | 2640 | } |
| 2618 | 2641 | }, |
| 2619 | | .memory => |vaddr| { |
| 2620 | | _ = vaddr; |
| 2621 | | return self.fail("TODO implement set stack variable from memory vaddr", .{}); |
| 2642 | .memory, |
| 2643 | .stack_argument_offset, |
| 2644 | => { |
| 2645 | if (ty.abiSize(self.target.*) <= 4) { |
| 2646 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2647 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 2648 | } else { |
| 2649 | return self.fail("TODO implement memcpy", .{}); |
| 2650 | } |
| 2622 | 2651 | }, |
| 2623 | 2652 | .stack_offset => |off| { |
| 2624 | 2653 | if (stack_offset == off) |
| 2625 | 2654 | return; // Copy stack variable to itself; nothing to do. |
| 2626 | 2655 | |
| 2627 | | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2628 | | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 2656 | if (ty.abiSize(self.target.*) <= 4) { |
| 2657 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2658 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 2659 | } else { |
| 2660 | return self.fail("TODO implement memcpy", .{}); |
| 2661 | } |
| 2629 | 2662 | }, |
| 2630 | 2663 | } |
| 2631 | 2664 | } |
| ... | ... | @@ -2878,10 +2911,115 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2878 | 2911 | else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}), |
| 2879 | 2912 | } |
| 2880 | 2913 | }, |
| 2914 | .stack_argument_offset => |unadjusted_off| { |
| 2915 | // TODO: maybe addressing from sp instead of fp |
| 2916 | const abi_size = ty.abiSize(self.target.*); |
| 2917 | const adj_off = unadjusted_off + abi_size; |
| 2918 | |
| 2919 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2920 | 1 => .ldrb_stack_argument, |
| 2921 | 2 => .ldrh_stack_argument, |
| 2922 | 4 => .ldr_stack_argument, |
| 2923 | else => unreachable, |
| 2924 | }; |
| 2925 | |
| 2926 | _ = try self.addInst(.{ |
| 2927 | .tag = tag, |
| 2928 | .cond = .al, |
| 2929 | .data = .{ .r_stack_offset = .{ |
| 2930 | .rt = reg, |
| 2931 | .stack_offset = @intCast(u32, adj_off), |
| 2932 | } }, |
| 2933 | }); |
| 2934 | }, |
| 2881 | 2935 | else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}), |
| 2882 | 2936 | } |
| 2883 | 2937 | } |
| 2884 | 2938 | |
| 2939 | fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 2940 | switch (mcv) { |
| 2941 | .dead => unreachable, |
| 2942 | .none, .unreach => return, |
| 2943 | .undef => { |
| 2944 | if (!self.wantSafety()) |
| 2945 | return; // The already existing value will do just fine. |
| 2946 | // TODO Upgrade this to a memset call when we have that available. |
| 2947 | switch (ty.abiSize(self.target.*)) { |
| 2948 | 1 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaa }), |
| 2949 | 2 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaa }), |
| 2950 | 4 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), |
| 2951 | 8 => return self.genSetStackArgument(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), |
| 2952 | else => return self.fail("TODO implement memset", .{}), |
| 2953 | } |
| 2954 | }, |
| 2955 | .register => |reg| { |
| 2956 | const abi_size = ty.abiSize(self.target.*); |
| 2957 | const adj_off = stack_offset - abi_size; |
| 2958 | |
| 2959 | switch (abi_size) { |
| 2960 | 1, 4 => { |
| 2961 | const offset = if (math.cast(u12, adj_off)) |imm| blk: { |
| 2962 | break :blk Instruction.Offset.imm(imm); |
| 2963 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); |
| 2964 | |
| 2965 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2966 | 1 => .strb, |
| 2967 | 4 => .str, |
| 2968 | else => unreachable, |
| 2969 | }; |
| 2970 | |
| 2971 | _ = try self.addInst(.{ |
| 2972 | .tag = tag, |
| 2973 | .cond = .al, |
| 2974 | .data = .{ .rr_offset = .{ |
| 2975 | .rt = reg, |
| 2976 | .rn = .sp, |
| 2977 | .offset = .{ .offset = offset }, |
| 2978 | } }, |
| 2979 | }); |
| 2980 | }, |
| 2981 | 2 => { |
| 2982 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2983 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2984 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); |
| 2985 | |
| 2986 | _ = try self.addInst(.{ |
| 2987 | .tag = .strh, |
| 2988 | .cond = .al, |
| 2989 | .data = .{ .rr_extra_offset = .{ |
| 2990 | .rt = reg, |
| 2991 | .rn = .sp, |
| 2992 | .offset = .{ .offset = offset }, |
| 2993 | } }, |
| 2994 | }); |
| 2995 | }, |
| 2996 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 2997 | } |
| 2998 | }, |
| 2999 | .immediate, |
| 3000 | .compare_flags_signed, |
| 3001 | .compare_flags_unsigned, |
| 3002 | .stack_offset, |
| 3003 | .memory, |
| 3004 | .stack_argument_offset, |
| 3005 | .embedded_in_code, |
| 3006 | => { |
| 3007 | if (ty.abiSize(self.target.*) <= 4) { |
| 3008 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 3009 | return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); |
| 3010 | } else { |
| 3011 | return self.fail("TODO implement memcpy", .{}); |
| 3012 | } |
| 3013 | }, |
| 3014 | .ptr_stack_offset => { |
| 3015 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 3016 | }, |
| 3017 | .ptr_embedded_in_code => { |
| 3018 | return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); |
| 3019 | }, |
| 3020 | } |
| 3021 | } |
| 3022 | |
| 2885 | 3023 | fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { |
| 2886 | 3024 | const un_op = self.air.instructions.items(.data)[inst].un_op; |
| 2887 | 3025 | const result = try self.resolveInst(un_op); |
| ... | ... | @@ -3002,27 +3140,6 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { |
| 3002 | 3140 | } |
| 3003 | 3141 | } |
| 3004 | 3142 | |
| 3005 | | /// If the MCValue is an immediate, and it does not fit within this type, |
| 3006 | | /// we put it in a register. |
| 3007 | | /// A potential opportunity for future optimization here would be keeping track |
| 3008 | | /// of the fact that the instruction is available both as an immediate |
| 3009 | | /// and as a register. |
| 3010 | | fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue { |
| 3011 | | const mcv = try self.resolveInst(operand); |
| 3012 | | const ti = @typeInfo(T).Int; |
| 3013 | | switch (mcv) { |
| 3014 | | .immediate => |imm| { |
| 3015 | | // This immediate is unsigned. |
| 3016 | | const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); |
| 3017 | | if (imm >= math.maxInt(U)) { |
| 3018 | | return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; |
| 3019 | | } |
| 3020 | | }, |
| 3021 | | else => {}, |
| 3022 | | } |
| 3023 | | return mcv; |
| 3024 | | } |
| 3025 | | |
| 3026 | 3143 | fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { |
| 3027 | 3144 | if (typed_value.val.isUndef()) |
| 3028 | 3145 | return MCValue{ .undef = {} }; |
| ... | ... | @@ -3214,7 +3331,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 3214 | 3331 | if (ty.abiAlignment(self.target.*) == 8) |
| 3215 | 3332 | nsaa = std.mem.alignForwardGeneric(u32, nsaa, 8); |
| 3216 | 3333 | |
| 3217 | | result.args[i] = .{ .stack_offset = nsaa }; |
| 3334 | result.args[i] = .{ .stack_argument_offset = nsaa }; |
| 3218 | 3335 | nsaa += param_size; |
| 3219 | 3336 | } |
| 3220 | 3337 | } |