diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 209 | ||||
| -rw-r--r-- | src/codegen/c.zig | 2 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 16 | ||||
| -rw-r--r-- | src/link/Elf.zig | 2 |
4 files changed, 167 insertions, 62 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index fb4c8a5745..b30f602280 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -177,7 +177,7 @@ pub const MCValue = union(enum) { /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register. register_overflow: struct { reg: Register, eflags: Condition }, /// The value is in memory at a hard-coded address. - /// If the type is a pointer, it means the pointer address is at this memory location. + /// If the type is a pointer, it means the pointer address is stored at this memory location. memory: u64, /// The value is in memory at a constant offset from the address in a register. indirect: RegisterOffset, @@ -300,7 +300,7 @@ pub const MCValue = union(enum) { .load_tlv, .load_frame, .reserved_frame, - => unreachable, // not a dereferenceable + => unreachable, // not dereferenceable .immediate => |addr| .{ .memory = addr }, .register => |reg| .{ .indirect = .{ .reg = reg } }, .register_offset => |reg_off| .{ .indirect = reg_off }, @@ -3468,14 +3468,14 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa const mod = self.bin_file.options.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); const int_info = ty.intInfo(mod); - const dividend: Register = switch (lhs) { + const dividend = switch (lhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, lhs), }; const dividend_lock = self.register_manager.lockReg(dividend); defer if (dividend_lock) |lock| self.register_manager.unlockReg(lock); - const divisor: Register = switch (rhs) { + const divisor = switch (rhs) { .register => |reg| reg, else => try self.copyToTmpRegister(ty, rhs), }; @@ -9184,6 +9184,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); const clobbers_len: u31 = @truncate(extra.data.flags); @@ -9196,23 +9197,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { var result: MCValue = .none; var args = std.StringArrayHashMap(MCValue).init(self.gpa); - try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len); + try args.ensureTotalCapacity(outputs.len + inputs.len); defer { - for (args.values()) |arg| switch (arg) { - .register => |reg| self.register_manager.unlockReg(.{ .register = reg }), - else => {}, - }; + for (args.values()) |arg| if (arg.getReg()) |reg| + self.register_manager.unlockReg(.{ .register = reg }); args.deinit(); } - if (outputs.len > 1) { - return self.fail("TODO implement codegen for asm with more than 1 output", .{}); - } - + var outputs_extra_i = extra_i; for (outputs) |output| { - if (output != .none) { - return self.fail("TODO implement codegen for non-expr asm", .{}); - } const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); @@ -9220,21 +9213,48 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - const mcv: MCValue = if (mem.eql(u8, constraint, "=r")) - .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse - return self.fail("ran out of registers lowering inline asm", .{}) } + const maybe_inst = switch (output) { + .none => inst, + else => null, + }; + const ty = switch (output) { + .none => self.typeOfIndex(inst), + else => self.typeOf(output).childType(mod), + }; + const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint, "=r")) + self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse + return self.fail("ran out of registers lowering inline asm", .{}) + else if (mem.eql(u8, constraint, "=m")) + if (output != .none) null else return self.fail( + "memory constraint unsupported for asm result", + .{}, + ) + else if (mem.eql(u8, constraint, "=g")) + self.register_manager.tryAllocReg(maybe_inst, regClassForType(ty, mod)) orelse + if (output != .none) null else return self.fail( + "ran out of register lowering inline asm", + .{}, + ) else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}")) - .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse - return self.fail("unrecognized register constraint: '{s}'", .{constraint}) } + parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse + return self.fail("invalid register constraint: '{s}'", .{constraint}) else - return self.fail("unrecognized constraint: '{s}'", .{constraint}); - args.putAssumeCapacity(name, mcv); - switch (mcv) { - .register => |reg| _ = if (RegisterManager.indexOfRegIntoTracked(reg)) |_| - self.register_manager.lockRegAssumeUnused(reg), - else => {}, - } - if (output == .none) result = mcv; + return self.fail("invalid constraint: '{s}'", .{constraint}); + const arg_mcv: MCValue = if (arg_maybe_reg) |reg| .{ .register = reg } else arg: { + const ptr_mcv = try self.resolveInst(output); + switch (ptr_mcv) { + .immediate => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| + break :arg ptr_mcv.deref(), + .register, .register_offset, .lea_frame => break :arg ptr_mcv.deref(), + else => {}, + } + break :arg .{ .indirect = .{ .reg = try self.copyToTmpRegister(Type.usize, ptr_mcv) } }; + }; + if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { + _ = self.register_manager.lockRegAssumeUnused(reg); + }; + args.putAssumeCapacity(name, arg_mcv); + if (output == .none) result = arg_mcv; } for (inputs) |input| { @@ -9245,16 +9265,53 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { // for the string, we still use the next u32 for the null terminator. extra_i += (constraint.len + name.len + (2 + 3)) / 4; - if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { - return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); - } - const reg_name = constraint[1 .. constraint.len - 1]; - const reg = parseRegName(reg_name) orelse - return self.fail("unrecognized register: '{s}'", .{reg_name}); - - const arg_mcv = try self.resolveInst(input); - try self.register_manager.getReg(reg, null); - try self.genSetReg(reg, self.typeOf(input), arg_mcv); + const ty = self.typeOf(input); + const input_mcv = try self.resolveInst(input); + const arg_mcv: MCValue = if (mem.eql(u8, constraint, "r")) switch (input_mcv) { + .register => input_mcv, + else => .{ .register = try self.copyToTmpRegister(ty, input_mcv) }, + } else if (mem.eql(u8, constraint, "m")) arg: { + switch (input_mcv) { + .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| + break :arg input_mcv, + .indirect, .load_frame => break :arg input_mcv, + .load_direct, .load_got, .load_tlv => {}, + else => { + const temp_mcv = try self.allocTempRegOrMem(ty, false); + try self.genCopy(ty, temp_mcv, input_mcv); + break :arg temp_mcv; + }, + } + const addr_reg = self.register_manager.tryAllocReg(null, gp) orelse { + const temp_mcv = try self.allocTempRegOrMem(ty, false); + try self.genCopy(ty, temp_mcv, input_mcv); + break :arg temp_mcv; + }; + try self.genSetReg(addr_reg, Type.usize, input_mcv.address()); + break :arg .{ .indirect = .{ .reg = addr_reg } }; + } else if (mem.eql(u8, constraint, "g")) arg: { + switch (input_mcv) { + .register, .indirect, .load_frame => break :arg input_mcv, + .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| + break :arg input_mcv, + else => {}, + } + const temp_mcv = try self.allocTempRegOrMem(ty, true); + try self.genCopy(ty, temp_mcv, input_mcv); + break :arg temp_mcv; + } else if (mem.eql(u8, constraint, "X")) + input_mcv + else if (mem.startsWith(u8, constraint, "{") and mem.endsWith(u8, constraint, "}")) arg: { + const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse + return self.fail("invalid register constraint: '{s}'", .{constraint}); + try self.register_manager.getReg(reg, null); + try self.genSetReg(reg, ty, input_mcv); + break :arg .{ .register = reg }; + } else return self.fail("invalid constraint: '{s}'", .{constraint}); + if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { + _ = self.register_manager.lockReg(reg); + }; + args.putAssumeCapacity(name, arg_mcv); } { @@ -9293,7 +9350,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } } break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse - return self.fail("Invalid mnemonic: '{s}'", .{mnem_str}); + return self.fail("invalid mnemonic: '{s}'", .{mnem_str}); } }; var op_it = mem.tokenizeScalar(u8, mnem_it.rest(), ','); @@ -9304,43 +9361,73 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { if (mem.startsWith(u8, op_str, "%%")) { const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':'); const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse - return self.fail("Invalid register: '{s}'", .{op_str}); + return self.fail("invalid register: '{s}'", .{op_str}); if (colon) |colon_pos| { const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch - return self.fail("Invalid displacement: '{s}'", .{op_str}); + return self.fail("invalid displacement: '{s}'", .{op_str}); op.* = .{ .mem = Memory.sib( - mnem_size orelse return self.fail("Unknown size: '{s}'", .{op_str}), + mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), .{ .base = .{ .reg = reg }, .disp = disp }, ) }; } else { if (mnem_size) |size| if (reg.bitSize() != size.bitSize()) - return self.fail("Invalid register size: '{s}'", .{op_str}); + return self.fail("invalid register size: '{s}'", .{op_str}); op.* = .{ .reg = reg }; } } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) { - switch (args.get(op_str["%[".len .. op_str.len - "]".len]) orelse - return self.fail("No matching constraint: '{s}'", .{op_str})) { - .register => |reg| op.* = .{ .reg = reg }, - else => return self.fail("Invalid constraint: '{s}'", .{op_str}), - } + const colon = mem.indexOfScalarPos(u8, op_str, "%[".len, ':'); + const modifier = if (colon) |colon_pos| + op_str[colon_pos + 1 .. op_str.len - "]".len] + else + ""; + op.* = switch (args.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse + return self.fail("no matching constraint: '{s}'", .{op_str})) { + .register => |reg| if (std.mem.eql(u8, modifier, "")) + .{ .reg = reg } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + .memory => |addr| if (std.mem.eql(u8, modifier, "") or + std.mem.eql(u8, modifier, "P")) + .{ .mem = Memory.sib( + mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), + .{ .base = .{ .reg = .ds }, .disp = @intCast(@as(i64, @bitCast(addr))) }, + ) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + .indirect => |reg_off| if (std.mem.eql(u8, modifier, "")) + .{ .mem = Memory.sib( + mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), + .{ .base = .{ .reg = reg_off.reg }, .disp = reg_off.off }, + ) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + .load_frame => |frame_addr| if (std.mem.eql(u8, modifier, "")) + .{ .mem = Memory.sib( + mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), + .{ .base = .{ .frame = frame_addr.index }, .disp = frame_addr.off }, + ) } + else + return self.fail("invalid modifier: '{s}'", .{modifier}), + else => return self.fail("invalid constraint: '{s}'", .{op_str}), + }; } else if (mem.startsWith(u8, op_str, "$")) { if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| { if (mnem_size) |size| { const max = @as(u64, math.maxInt(u64)) >> @intCast(64 - (size.bitSize() - 1)); if ((if (s < 0) ~s else s) > max) - return self.fail("Invalid immediate size: '{s}'", .{op_str}); + return self.fail("invalid immediate size: '{s}'", .{op_str}); } op.* = .{ .imm = Immediate.s(s) }; } else |_| if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| { if (mnem_size) |size| { const max = @as(u64, math.maxInt(u64)) >> @intCast(64 - size.bitSize()); if (u > max) - return self.fail("Invalid immediate size: '{s}'", .{op_str}); + return self.fail("invalid immediate size: '{s}'", .{op_str}); } op.* = .{ .imm = Immediate.u(u) }; - } else |_| return self.fail("Invalid immediate: '{s}'", .{op_str}); - } else return self.fail("Invalid operand: '{s}'", .{op_str}); - } else if (op_it.next()) |op_str| return self.fail("Extra operand: '{s}'", .{op_str}); + } else |_| return self.fail("invalid immediate: '{s}'", .{op_str}); + } else return self.fail("invalid operand: '{s}'", .{op_str}); + } else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str}); (switch (ops[0]) { .none => self.asmOpOnly(mnem_tag), @@ -9407,6 +9494,20 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { }; } + for (outputs, args.values()[0..outputs.len]) |output, mcv| { + const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); + const constraint = + std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); + const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); + // This equation accounts for the fact that even if we have exactly 4 bytes + // for the string, we still use the next u32 for the null terminator. + outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; + + if (output == .none) continue; + if (mcv != .register) continue; + try self.store(self.typeOf(output), try self.resolveInst(output), mcv); + } + simple: { var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); var buf_index: usize = 0; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 1ae9527f04..a11cd92827 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -4770,9 +4770,11 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { } fn asmInputNeedsLocal(f: *Function, constraint: []const u8, value: CValue) bool { + const target = f.object.dg.module.getTarget(); return switch (constraint[0]) { '{' => true, 'i', 'r' => false, + 'I' => !target.cpu.arch.isArmOrThumb(), else => switch (value) { .constant => |val| switch (f.object.dg.module.intern_pool.indexToKey(val)) { .ptr => |ptr| switch (ptr.addr) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 06c2f714d0..687d1dfb80 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -417,7 +417,7 @@ const DataLayoutBuilder = struct { if (idx != size) try writer.print(":{d}", .{idx}); } } - if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) + if (self.target.cpu.arch.isArmOrThumb()) try writer.writeAll("-Fi8"); // for thumb interwork if (self.target.cpu.arch != .hexagon) { if (self.target.cpu.arch == .s390x) try self.typeAlignment(.integer, 1, 8, 8, false, writer); @@ -620,7 +620,7 @@ const DataLayoutBuilder = struct { else => {}, } }, - .vector => if (self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) { + .vector => if (self.target.cpu.arch.isArmOrThumb()) { switch (size) { 128 => abi = 64, else => {}, @@ -670,7 +670,7 @@ const DataLayoutBuilder = struct { else => {}, }, .aggregate => if (self.target.os.tag == .uefi or self.target.os.tag == .windows or - self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) + self.target.cpu.arch.isArmOrThumb()) { pref = @min(pref, self.target.ptrBitWidth()); } else if (self.target.cpu.arch == .hexagon) { @@ -6809,8 +6809,6 @@ pub const FuncGen = struct { } llvm_constraints.appendAssumeCapacity('='); - // Pass any non-return outputs indirectly, if the constraint accepts a memory location - is_indirect.* = (output != .none) and constraintAllowsMemory(constraint); if (output != .none) { const output_inst = try self.resolveInst(output); const output_ty = self.typeOf(output); @@ -6825,6 +6823,8 @@ pub const FuncGen = struct { }), } + // Pass any non-return outputs indirectly, if the constraint accepts a memory location + is_indirect.* = constraintAllowsMemory(constraint); if (is_indirect.*) { // Pass the result by reference as an indirect output (e.g. "=*m") llvm_constraints.appendAssumeCapacity('*'); @@ -6841,11 +6841,13 @@ pub const FuncGen = struct { } else { switch (constraint[0]) { '=' => {}, - else => return self.todo("unsupported output constraint on result type '{c}'", .{ - constraint[0], + else => return self.todo("unsupported output constraint on result type '{s}'", .{ + constraint, }), } + is_indirect.* = false; + const ret_ty = self.typeOfIndex(inst); llvm_ret_types[llvm_ret_i] = try o.lowerType(ret_ty); llvm_ret_i += 1; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ec4da9c728..c4371fbcbf 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1654,7 +1654,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } if (self.base.options.link_mode == .Static) { - if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) { + if (target.cpu.arch.isArmOrThumb()) { try argv.append("-Bstatic"); } else { try argv.append("-static"); |
