| ... | ... | @@ -32,10 +32,10 @@ const Target = std.Target; |
| 32 | 32 | const Type = @import("../../type.zig").Type; |
| 33 | 33 | const TypedValue = @import("../../TypedValue.zig"); |
| 34 | 34 | const Value = @import("../../value.zig").Value; |
| 35 | const Instruction = @import("encoder.zig").Instruction; |
| 35 | 36 | |
| 36 | 37 | const abi = @import("abi.zig"); |
| 37 | 38 | const bits = @import("bits.zig"); |
| 38 | | const encoder = @import("encoder.zig"); |
| 39 | 39 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 40 | 40 | const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 41 | 41 | |
| ... | ... | @@ -1275,6 +1275,17 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins |
| 1275 | 1275 | }); |
| 1276 | 1276 | } |
| 1277 | 1277 | |
| 1278 | fn asmReloc(self: *Self, tag: Mir.Inst.FixedTag, target: Mir.Inst.Index) !void { |
| 1279 | _ = try self.addInst(.{ |
| 1280 | .tag = tag[1], |
| 1281 | .ops = .inst, |
| 1282 | .data = .{ .inst = .{ |
| 1283 | .fixes = tag[0], |
| 1284 | .inst = target, |
| 1285 | } }, |
| 1286 | }); |
| 1287 | } |
| 1288 | |
| 1278 | 1289 | fn asmPlaceholder(self: *Self) !Mir.Inst.Index { |
| 1279 | 1290 | return self.addInst(.{ |
| 1280 | 1291 | .tag = .pseudo, |
| ... | ... | @@ -2174,7 +2185,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { |
| 2174 | 2185 | const ret_reg = param_regs[0]; |
| 2175 | 2186 | const enum_mcv = MCValue{ .register = param_regs[1] }; |
| 2176 | 2187 | |
| 2177 | | var exitlude_jump_relocs = try self.gpa.alloc(u32, enum_ty.enumFieldCount(mod)); |
| 2188 | var exitlude_jump_relocs = try self.gpa.alloc(Mir.Inst.Index, enum_ty.enumFieldCount(mod)); |
| 2178 | 2189 | defer self.gpa.free(exitlude_jump_relocs); |
| 2179 | 2190 | |
| 2180 | 2191 | const data_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); |
| ... | ... | @@ -5887,7 +5898,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr |
| 5887 | 5898 | |
| 5888 | 5899 | try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv); |
| 5889 | 5900 | }, |
| 5890 | | .air_ref => |ptr_ref| try self.store(ptr_ty, ptr_mcv, try self.resolveInst(ptr_ref)), |
| 5901 | .air_ref => |ptr_ref| try self.store(ptr_ty, try self.resolveInst(ptr_ref), src_mcv), |
| 5891 | 5902 | } |
| 5892 | 5903 | } |
| 5893 | 5904 | |
| ... | ... | @@ -9313,7 +9324,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 9313 | 9324 | return self.finishAir(inst, .unreach, .{ operand, .none, .none }); |
| 9314 | 9325 | } |
| 9315 | 9326 | |
| 9316 | | fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !u32 { |
| 9327 | fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index { |
| 9317 | 9328 | const mod = self.bin_file.options.module.?; |
| 9318 | 9329 | const abi_size = ty.abiSize(mod); |
| 9319 | 9330 | switch (mcv) { |
| ... | ... | @@ -9695,7 +9706,7 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 9695 | 9706 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 9696 | 9707 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 9697 | 9708 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 9698 | | const jmp_target: u32 = @intCast(self.mir_instructions.len); |
| 9709 | const jmp_target: Mir.Inst.Index = @intCast(self.mir_instructions.len); |
| 9699 | 9710 | |
| 9700 | 9711 | self.scope_generation += 1; |
| 9701 | 9712 | const state = try self.saveState(); |
| ... | ... | @@ -9772,7 +9783,7 @@ fn airSwitchBr(self: *Self, inst: Air.Inst.Index) !void { |
| 9772 | 9783 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| 9773 | 9784 | extra_index = case.end + items.len + case_body.len; |
| 9774 | 9785 | |
| 9775 | | var relocs = try self.gpa.alloc(u32, items.len); |
| 9786 | var relocs = try self.gpa.alloc(Mir.Inst.Index, items.len); |
| 9776 | 9787 | defer self.gpa.free(relocs); |
| 9777 | 9788 | |
| 9778 | 9789 | try self.spillEflagsIfOccupied(); |
| ... | ... | @@ -9929,22 +9940,35 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9929 | 9940 | .none => self.typeOfIndex(inst), |
| 9930 | 9941 | else => self.typeOf(output).childType(mod), |
| 9931 | 9942 | }; |
| 9932 | | const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint, "=r")) |
| 9943 | const is_read = switch (constraint[0]) { |
| 9944 | '=' => false, |
| 9945 | '+' => read: { |
| 9946 | if (output == .none) return self.fail( |
| 9947 | "read-write constraint unsupported for asm result: '{s}'", |
| 9948 | .{constraint}, |
| 9949 | ); |
| 9950 | break :read true; |
| 9951 | }, |
| 9952 | else => return self.fail("invalid constraint: '{s}'", .{constraint}), |
| 9953 | }; |
| 9954 | const arg_maybe_reg: ?Register = if (mem.eql(u8, constraint[1..], "r")) |
| 9933 | 9955 | self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse |
| 9934 | 9956 | return self.fail("ran out of registers lowering inline asm", .{}) |
| 9935 | | else if (mem.eql(u8, constraint, "=m")) |
| 9957 | else if (mem.eql(u8, constraint[1..], "m")) |
| 9936 | 9958 | if (output != .none) null else return self.fail( |
| 9937 | | "memory constraint unsupported for asm result", |
| 9938 | | .{}, |
| 9959 | "memory constraint unsupported for asm result: '{s}'", |
| 9960 | .{constraint}, |
| 9939 | 9961 | ) |
| 9940 | | else if (mem.eql(u8, constraint, "=g")) |
| 9962 | else if (mem.eql(u8, constraint[1..], "g") or |
| 9963 | mem.eql(u8, constraint[1..], "rm") or mem.eql(u8, constraint[1..], "mr") or |
| 9964 | mem.eql(u8, constraint[1..], "r,m") or mem.eql(u8, constraint[1..], "m,r")) |
| 9941 | 9965 | self.register_manager.tryAllocReg(maybe_inst, self.regClassForType(ty)) orelse |
| 9942 | | if (output != .none) null else return self.fail( |
| 9943 | | "ran out of register lowering inline asm", |
| 9944 | | .{}, |
| 9945 | | ) |
| 9946 | | else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}")) |
| 9947 | | parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse |
| 9966 | if (output != .none) |
| 9967 | null |
| 9968 | else |
| 9969 | return self.fail("ran out of registers lowering inline asm", .{}) |
| 9970 | else if (mem.startsWith(u8, constraint[1..], "{") and mem.endsWith(u8, constraint[1..], "}")) |
| 9971 | parseRegName(constraint[1 + "{".len .. constraint.len - "}".len]) orelse |
| 9948 | 9972 | return self.fail("invalid register constraint: '{s}'", .{constraint}) |
| 9949 | 9973 | else |
| 9950 | 9974 | return self.fail("invalid constraint: '{s}'", .{constraint}); |
| ... | ... | @@ -9965,6 +9989,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9965 | 9989 | arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); |
| 9966 | 9990 | args.appendAssumeCapacity(arg_mcv); |
| 9967 | 9991 | if (output == .none) result = arg_mcv; |
| 9992 | if (is_read) try self.load(arg_mcv, self.typeOf(output), .{ .air_ref = output }); |
| 9968 | 9993 | } |
| 9969 | 9994 | |
| 9970 | 9995 | for (inputs) |input| { |
| ... | ... | @@ -9999,7 +10024,10 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 9999 | 10024 | }; |
| 10000 | 10025 | try self.genSetReg(addr_reg, Type.usize, input_mcv.address()); |
| 10001 | 10026 | break :arg .{ .indirect = .{ .reg = addr_reg } }; |
| 10002 | | } else if (mem.eql(u8, constraint, "g")) arg: { |
| 10027 | } else if (mem.eql(u8, constraint, "g") or |
| 10028 | mem.eql(u8, constraint, "rm") or mem.eql(u8, constraint, "mr") or |
| 10029 | mem.eql(u8, constraint, "r,m") or mem.eql(u8, constraint, "m,r")) |
| 10030 | arg: { |
| 10003 | 10031 | switch (input_mcv) { |
| 10004 | 10032 | .register, .indirect, .load_frame => break :arg input_mcv, |
| 10005 | 10033 | .memory => |addr| if (math.cast(i32, @as(i64, @bitCast(addr)))) |_| |
| ... | ... | @@ -10038,44 +10066,141 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10038 | 10066 | } |
| 10039 | 10067 | } |
| 10040 | 10068 | |
| 10069 | const Label = struct { |
| 10070 | target: Mir.Inst.Index = undefined, |
| 10071 | pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}, |
| 10072 | |
| 10073 | const Kind = enum { definition, reference }; |
| 10074 | |
| 10075 | fn isValid(kind: Kind, name: []const u8) bool { |
| 10076 | for (name, 0..) |c, i| switch (c) { |
| 10077 | else => return false, |
| 10078 | '$' => if (i == 0) return false, |
| 10079 | '.' => {}, |
| 10080 | '0'...'9' => if (i == 0) switch (kind) { |
| 10081 | .definition => if (name.len != 1) return false, |
| 10082 | .reference => { |
| 10083 | if (name.len != 2) return false; |
| 10084 | switch (name[1]) { |
| 10085 | else => return false, |
| 10086 | 'B', 'F', 'b', 'f' => {}, |
| 10087 | } |
| 10088 | }, |
| 10089 | }, |
| 10090 | '@', 'A'...'Z', '_', 'a'...'z' => {}, |
| 10091 | }; |
| 10092 | return name.len > 0; |
| 10093 | } |
| 10094 | }; |
| 10095 | var labels: std.StringHashMapUnmanaged(Label) = .{}; |
| 10096 | defer { |
| 10097 | var label_it = labels.valueIterator(); |
| 10098 | while (label_it.next()) |label| label.pending_relocs.deinit(self.gpa); |
| 10099 | labels.deinit(self.gpa); |
| 10100 | } |
| 10101 | |
| 10041 | 10102 | const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; |
| 10042 | 10103 | var line_it = mem.tokenizeAny(u8, asm_source, "\n\r;"); |
| 10043 | | while (line_it.next()) |line| { |
| 10104 | next_line: while (line_it.next()) |line| { |
| 10044 | 10105 | var mnem_it = mem.tokenizeAny(u8, line, " \t"); |
| 10045 | | const mnem_str = mnem_it.next() orelse continue; |
| 10046 | | if (mem.startsWith(u8, mnem_str, "#")) continue; |
| 10047 | | |
| 10048 | | const mnem_size: ?Memory.PtrSize = if (mem.endsWith(u8, mnem_str, "b")) |
| 10049 | | .byte |
| 10050 | | else if (mem.endsWith(u8, mnem_str, "w")) |
| 10051 | | .word |
| 10052 | | else if (mem.endsWith(u8, mnem_str, "l")) |
| 10053 | | .dword |
| 10054 | | else if (mem.endsWith(u8, mnem_str, "q")) |
| 10055 | | .qword |
| 10056 | | else |
| 10057 | | null; |
| 10058 | | const mnem_tag = Mir.Inst.FixedTag{ ._, mnem: { |
| 10059 | | if (mnem_size) |_| { |
| 10060 | | if (std.meta.stringToEnum(Mir.Inst.Tag, mnem_str[0 .. mnem_str.len - 1])) |mnem| { |
| 10061 | | break :mnem mnem; |
| 10062 | | } |
| 10106 | var prefix: Instruction.Prefix = .none; |
| 10107 | const mnem_str = while (mnem_it.next()) |mnem_str| { |
| 10108 | if (mem.startsWith(u8, mnem_str, "#")) continue :next_line; |
| 10109 | if (std.meta.stringToEnum(Instruction.Prefix, mnem_str)) |pre| { |
| 10110 | if (prefix != .none) return self.fail("extra prefix: '{s}'", .{mnem_str}); |
| 10111 | prefix = pre; |
| 10112 | continue; |
| 10063 | 10113 | } |
| 10064 | | break :mnem std.meta.stringToEnum(Mir.Inst.Tag, mnem_str) orelse |
| 10065 | | return self.fail("invalid mnemonic: '{s}'", .{mnem_str}); |
| 10066 | | } }; |
| 10067 | | |
| 10068 | | var op_it = mem.tokenizeScalar(u8, mnem_it.rest(), ','); |
| 10069 | | var ops = [1]encoder.Instruction.Operand{.none} ** 4; |
| 10070 | | for (&ops) |*op| { |
| 10071 | | const op_str = mem.trim(u8, op_it.next() orelse break, " \t"); |
| 10072 | | if (mem.startsWith(u8, op_str, "#")) break; |
| 10114 | if (!mem.endsWith(u8, mnem_str, ":")) break mnem_str; |
| 10115 | const label_name = mnem_str[0 .. mnem_str.len - ":".len]; |
| 10116 | if (!Label.isValid(.definition, label_name)) |
| 10117 | return self.fail("invalid label: '{s}'", .{label_name}); |
| 10118 | const label_gop = try labels.getOrPut(self.gpa, label_name); |
| 10119 | if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else { |
| 10120 | const anon = std.ascii.isDigit(label_name[0]); |
| 10121 | if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0) |
| 10122 | return self.fail("redefined label: '{s}'", .{label_name}); |
| 10123 | for (label_gop.value_ptr.pending_relocs.items) |pending_reloc| |
| 10124 | try self.performReloc(pending_reloc); |
| 10125 | if (anon) |
| 10126 | label_gop.value_ptr.pending_relocs.clearRetainingCapacity() |
| 10127 | else |
| 10128 | label_gop.value_ptr.pending_relocs.clearAndFree(self.gpa); |
| 10129 | } |
| 10130 | label_gop.value_ptr.target = @intCast(self.mir_instructions.len); |
| 10131 | } else continue; |
| 10132 | |
| 10133 | var mnem_size: ?Memory.PtrSize = null; |
| 10134 | const mnem_tag = mnem: { |
| 10135 | mnem_size = if (mem.endsWith(u8, mnem_str, "b")) |
| 10136 | .byte |
| 10137 | else if (mem.endsWith(u8, mnem_str, "w")) |
| 10138 | .word |
| 10139 | else if (mem.endsWith(u8, mnem_str, "l")) |
| 10140 | .dword |
| 10141 | else if (mem.endsWith(u8, mnem_str, "q")) |
| 10142 | .qword |
| 10143 | else if (mem.endsWith(u8, mnem_str, "t")) |
| 10144 | .tbyte |
| 10145 | else |
| 10146 | break :mnem null; |
| 10147 | break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str[0 .. mnem_str.len - 1]); |
| 10148 | } orelse mnem: { |
| 10149 | mnem_size = null; |
| 10150 | break :mnem std.meta.stringToEnum(Instruction.Mnemonic, mnem_str); |
| 10151 | } orelse return self.fail("invalid mnemonic: '{s}'", .{mnem_str}); |
| 10152 | const mnem_name = @tagName(mnem_tag); |
| 10153 | const mnem_fixed_tag: Mir.Inst.FixedTag = for (std.enums.values(Mir.Inst.Fixes)) |fixes| { |
| 10154 | const fixes_name = @tagName(fixes); |
| 10155 | const space_i = mem.indexOfScalar(u8, fixes_name, ' '); |
| 10156 | const fixes_prefix = if (space_i) |i| |
| 10157 | std.meta.stringToEnum(Instruction.Prefix, fixes_name[0..i]).? |
| 10158 | else |
| 10159 | .none; |
| 10160 | if (fixes_prefix != prefix) continue; |
| 10161 | const pattern = fixes_name[if (space_i) |i| i + " ".len else 0..]; |
| 10162 | const wildcard_i = mem.indexOfScalar(u8, pattern, '_').?; |
| 10163 | const mnem_prefix = pattern[0..wildcard_i]; |
| 10164 | const mnem_suffix = pattern[wildcard_i + "_".len ..]; |
| 10165 | if (!mem.startsWith(u8, mnem_name, mnem_prefix)) continue; |
| 10166 | if (!mem.endsWith(u8, mnem_name, mnem_suffix)) continue; |
| 10167 | break .{ fixes, std.meta.stringToEnum( |
| 10168 | Mir.Inst.Tag, |
| 10169 | mnem_name[mnem_prefix.len .. mnem_name.len - mnem_suffix.len], |
| 10170 | ) orelse continue }; |
| 10171 | } else { |
| 10172 | assert(prefix != .none); |
| 10173 | return self.fail("invalid prefix for mnemonic: '{s} {s}'", .{ |
| 10174 | @tagName(prefix), mnem_str, |
| 10175 | }); |
| 10176 | }; |
| 10177 | |
| 10178 | const Operand = union(enum) { |
| 10179 | none, |
| 10180 | reg: Register, |
| 10181 | mem: Memory, |
| 10182 | imm: Immediate, |
| 10183 | inst: Mir.Inst.Index, |
| 10184 | }; |
| 10185 | var ops: [4]Operand = .{.none} ** 4; |
| 10186 | |
| 10187 | var last_op = false; |
| 10188 | var op_it = mem.splitScalar(u8, mnem_it.rest(), ','); |
| 10189 | next_op: for (&ops) |*op| { |
| 10190 | const op_str = while (!last_op) { |
| 10191 | const full_str = op_it.next() orelse break :next_op; |
| 10192 | const trim_str = mem.trim(u8, if (mem.indexOfScalar(u8, full_str, '#')) |hash| hash: { |
| 10193 | last_op = true; |
| 10194 | break :hash full_str[0..hash]; |
| 10195 | } else full_str, " \t"); |
| 10196 | if (trim_str.len > 0) break trim_str; |
| 10197 | } else break; |
| 10073 | 10198 | if (mem.startsWith(u8, op_str, "%%")) { |
| 10074 | 10199 | const colon = mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':'); |
| 10075 | 10200 | const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse |
| 10076 | 10201 | return self.fail("invalid register: '{s}'", .{op_str}); |
| 10077 | 10202 | if (colon) |colon_pos| { |
| 10078 | | const disp = std.fmt.parseInt(i32, op_str[colon_pos + 1 ..], 0) catch |
| 10203 | const disp = std.fmt.parseInt(i32, op_str[colon_pos + ":".len ..], 0) catch |
| 10079 | 10204 | return self.fail("invalid displacement: '{s}'", .{op_str}); |
| 10080 | 10205 | op.* = .{ .mem = Memory.sib( |
| 10081 | 10206 | mnem_size orelse return self.fail("unknown size: '{s}'", .{op_str}), |
| ... | ... | @@ -10089,7 +10214,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10089 | 10214 | } else if (mem.startsWith(u8, op_str, "%[") and mem.endsWith(u8, op_str, "]")) { |
| 10090 | 10215 | const colon = mem.indexOfScalarPos(u8, op_str, "%[".len, ':'); |
| 10091 | 10216 | const modifier = if (colon) |colon_pos| |
| 10092 | | op_str[colon_pos + 1 .. op_str.len - "]".len] |
| 10217 | op_str[colon_pos + ":".len .. op_str.len - "]".len] |
| 10093 | 10218 | else |
| 10094 | 10219 | ""; |
| 10095 | 10220 | op.* = switch (args.items[ |
| ... | ... | @@ -10140,64 +10265,113 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10140 | 10265 | } |
| 10141 | 10266 | op.* = .{ .imm = Immediate.u(u) }; |
| 10142 | 10267 | } else |_| return self.fail("invalid immediate: '{s}'", .{op_str}); |
| 10268 | } else if (mem.endsWith(u8, op_str, ")")) { |
| 10269 | const open = mem.indexOfScalar(u8, op_str, '(') orelse |
| 10270 | return self.fail("invalid operand: '{s}'", .{op_str}); |
| 10271 | var sib_it = mem.splitScalar(u8, op_str[open + "(".len .. op_str.len - ")".len], ','); |
| 10272 | const base_str = sib_it.next() orelse |
| 10273 | return self.fail("invalid memory operand: '{s}'", .{op_str}); |
| 10274 | if (base_str.len > 0 and !mem.startsWith(u8, base_str, "%%")) |
| 10275 | return self.fail("invalid memory operand: '{s}'", .{op_str}); |
| 10276 | const index_str = sib_it.next() orelse ""; |
| 10277 | if (index_str.len > 0 and !mem.startsWith(u8, base_str, "%%")) |
| 10278 | return self.fail("invalid memory operand: '{s}'", .{op_str}); |
| 10279 | const scale_str = sib_it.next() orelse ""; |
| 10280 | if (index_str.len == 0 and scale_str.len > 0) |
| 10281 | return self.fail("invalid memory operand: '{s}'", .{op_str}); |
| 10282 | const scale = if (scale_str.len > 0) switch (std.fmt.parseInt(u4, scale_str, 10) catch |
| 10283 | return self.fail("invalid scale: '{s}'", .{op_str})) { |
| 10284 | 1, 2, 4, 8 => |scale| scale, |
| 10285 | else => return self.fail("invalid scale: '{s}'", .{op_str}), |
| 10286 | } else 1; |
| 10287 | if (sib_it.next()) |_| return self.fail("invalid memory operand: '{s}'", .{op_str}); |
| 10288 | op.* = .{ .mem = Memory.sib(mnem_size orelse |
| 10289 | return self.fail("unknown size: '{s}'", .{op_str}), .{ |
| 10290 | .disp = if (open > 0) std.fmt.parseInt(i32, op_str[0..open], 0) catch |
| 10291 | return self.fail("invalid displacement: '{s}'", .{op_str}) else 0, |
| 10292 | .base = if (base_str.len > 0) .{ .reg = parseRegName(base_str["%%".len..]) orelse |
| 10293 | return self.fail("invalid base register: '{s}'", .{base_str}) } else .none, |
| 10294 | .scale_index = if (index_str.len > 0) .{ |
| 10295 | .index = parseRegName(index_str["%%".len..]) orelse |
| 10296 | return self.fail("invalid index register: '{s}'", .{op_str}), |
| 10297 | .scale = scale, |
| 10298 | } else null, |
| 10299 | }) }; |
| 10300 | } else if (Label.isValid(.reference, op_str)) { |
| 10301 | const anon = std.ascii.isDigit(op_str[0]); |
| 10302 | const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else op_str.len]); |
| 10303 | if (!label_gop.found_existing) label_gop.value_ptr.* = .{}; |
| 10304 | if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing) |
| 10305 | return self.fail("undefined label: '{s}'", .{op_str}); |
| 10306 | const pending_relocs = &label_gop.value_ptr.pending_relocs; |
| 10307 | if (if (anon) |
| 10308 | op_str[1] == 'f' or op_str[1] == 'F' |
| 10309 | else |
| 10310 | !label_gop.found_existing or pending_relocs.items.len > 0) |
| 10311 | try pending_relocs.append(self.gpa, @intCast(self.mir_instructions.len)); |
| 10312 | op.* = .{ .inst = label_gop.value_ptr.target }; |
| 10143 | 10313 | } else return self.fail("invalid operand: '{s}'", .{op_str}); |
| 10144 | 10314 | } else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str}); |
| 10145 | 10315 | |
| 10146 | 10316 | (switch (ops[0]) { |
| 10147 | | .none => self.asmOpOnly(mnem_tag), |
| 10317 | .none => self.asmOpOnly(mnem_fixed_tag), |
| 10148 | 10318 | .reg => |reg0| switch (ops[1]) { |
| 10149 | | .none => self.asmRegister(mnem_tag, reg0), |
| 10319 | .none => self.asmRegister(mnem_fixed_tag, reg0), |
| 10150 | 10320 | .reg => |reg1| switch (ops[2]) { |
| 10151 | | .none => self.asmRegisterRegister(mnem_tag, reg1, reg0), |
| 10321 | .none => self.asmRegisterRegister(mnem_fixed_tag, reg1, reg0), |
| 10152 | 10322 | .reg => |reg2| switch (ops[3]) { |
| 10153 | | .none => self.asmRegisterRegisterRegister(mnem_tag, reg2, reg1, reg0), |
| 10323 | .none => self.asmRegisterRegisterRegister(mnem_fixed_tag, reg2, reg1, reg0), |
| 10154 | 10324 | else => error.InvalidInstruction, |
| 10155 | 10325 | }, |
| 10156 | 10326 | .mem => |mem2| switch (ops[3]) { |
| 10157 | | .none => self.asmMemoryRegisterRegister(mnem_tag, mem2, reg1, reg0), |
| 10327 | .none => self.asmMemoryRegisterRegister(mnem_fixed_tag, mem2, reg1, reg0), |
| 10158 | 10328 | else => error.InvalidInstruction, |
| 10159 | 10329 | }, |
| 10160 | 10330 | else => error.InvalidInstruction, |
| 10161 | 10331 | }, |
| 10162 | 10332 | .mem => |mem1| switch (ops[2]) { |
| 10163 | | .none => self.asmMemoryRegister(mnem_tag, mem1, reg0), |
| 10333 | .none => self.asmMemoryRegister(mnem_fixed_tag, mem1, reg0), |
| 10164 | 10334 | else => error.InvalidInstruction, |
| 10165 | 10335 | }, |
| 10166 | 10336 | else => error.InvalidInstruction, |
| 10167 | 10337 | }, |
| 10168 | 10338 | .mem => |mem0| switch (ops[1]) { |
| 10169 | | .none => self.asmMemory(mnem_tag, mem0), |
| 10339 | .none => self.asmMemory(mnem_fixed_tag, mem0), |
| 10170 | 10340 | .reg => |reg1| switch (ops[2]) { |
| 10171 | | .none => self.asmRegisterMemory(mnem_tag, reg1, mem0), |
| 10341 | .none => self.asmRegisterMemory(mnem_fixed_tag, reg1, mem0), |
| 10172 | 10342 | else => error.InvalidInstruction, |
| 10173 | 10343 | }, |
| 10174 | 10344 | else => error.InvalidInstruction, |
| 10175 | 10345 | }, |
| 10176 | 10346 | .imm => |imm0| switch (ops[1]) { |
| 10177 | | .none => self.asmImmediate(mnem_tag, imm0), |
| 10347 | .none => self.asmImmediate(mnem_fixed_tag, imm0), |
| 10178 | 10348 | .reg => |reg1| switch (ops[2]) { |
| 10179 | | .none => self.asmRegisterImmediate(mnem_tag, reg1, imm0), |
| 10349 | .none => self.asmRegisterImmediate(mnem_fixed_tag, reg1, imm0), |
| 10180 | 10350 | .reg => |reg2| switch (ops[3]) { |
| 10181 | | .none => self.asmRegisterRegisterImmediate(mnem_tag, reg2, reg1, imm0), |
| 10351 | .none => self.asmRegisterRegisterImmediate(mnem_fixed_tag, reg2, reg1, imm0), |
| 10182 | 10352 | else => error.InvalidInstruction, |
| 10183 | 10353 | }, |
| 10184 | 10354 | .mem => |mem2| switch (ops[3]) { |
| 10185 | | .none => self.asmMemoryRegisterImmediate(mnem_tag, mem2, reg1, imm0), |
| 10355 | .none => self.asmMemoryRegisterImmediate(mnem_fixed_tag, mem2, reg1, imm0), |
| 10186 | 10356 | else => error.InvalidInstruction, |
| 10187 | 10357 | }, |
| 10188 | 10358 | else => error.InvalidInstruction, |
| 10189 | 10359 | }, |
| 10190 | 10360 | .mem => |mem1| switch (ops[2]) { |
| 10191 | | .none => self.asmMemoryImmediate(mnem_tag, mem1, imm0), |
| 10361 | .none => self.asmMemoryImmediate(mnem_fixed_tag, mem1, imm0), |
| 10192 | 10362 | else => error.InvalidInstruction, |
| 10193 | 10363 | }, |
| 10194 | 10364 | else => error.InvalidInstruction, |
| 10195 | 10365 | }, |
| 10366 | .inst => |inst0| switch (ops[1]) { |
| 10367 | .none => self.asmReloc(mnem_fixed_tag, inst0), |
| 10368 | else => error.InvalidInstruction, |
| 10369 | }, |
| 10196 | 10370 | }) catch |err| switch (err) { |
| 10197 | 10371 | error.InvalidInstruction => return self.fail( |
| 10198 | | "Invalid instruction: '{s} {s} {s} {s} {s}'", |
| 10372 | "invalid instruction: '{s} {s} {s} {s} {s}'", |
| 10199 | 10373 | .{ |
| 10200 | | @tagName(mnem_tag[1]), |
| 10374 | mnem_str, |
| 10201 | 10375 | @tagName(ops[0]), |
| 10202 | 10376 | @tagName(ops[1]), |
| 10203 | 10377 | @tagName(ops[2]), |
| ... | ... | @@ -10208,7 +10382,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10208 | 10382 | }; |
| 10209 | 10383 | } |
| 10210 | 10384 | |
| 10211 | | for (outputs, args.items[0..outputs.len]) |output, mcv| { |
| 10385 | var label_it = labels.iterator(); |
| 10386 | while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0) |
| 10387 | return self.fail("undefined label: '{s}'", .{label.key_ptr.*}); |
| 10388 | |
| 10389 | for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { |
| 10212 | 10390 | const extra_bytes = mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); |
| 10213 | 10391 | const constraint = |
| 10214 | 10392 | mem.sliceTo(mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); |
| ... | ... | @@ -10218,8 +10396,8 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 10218 | 10396 | outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 10219 | 10397 | |
| 10220 | 10398 | if (output == .none) continue; |
| 10221 | | if (mcv != .register) continue; |
| 10222 | | try self.store(self.typeOf(output), try self.resolveInst(output), mcv); |
| 10399 | if (arg_mcv != .register) continue; |
| 10400 | try self.store(self.typeOf(output), .{ .air_ref = output }, arg_mcv); |
| 10223 | 10401 | } |
| 10224 | 10402 | |
| 10225 | 10403 | simple: { |
| ... | ... | @@ -11510,7 +11688,7 @@ fn atomicOp( |
| 11510 | 11688 | defer self.register_manager.unlockReg(tmp_lock); |
| 11511 | 11689 | |
| 11512 | 11690 | try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(.rax, val_abi_size), ptr_mem); |
| 11513 | | const loop: u32 = @intCast(self.mir_instructions.len); |
| 11691 | const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); |
| 11514 | 11692 | if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { |
| 11515 | 11693 | try self.genSetReg(tmp_reg, val_ty, .{ .register = .rax }); |
| 11516 | 11694 | } |
| ... | ... | @@ -11584,7 +11762,7 @@ fn atomicOp( |
| 11584 | 11762 | .scale_index = ptr_mem.scaleIndex(), |
| 11585 | 11763 | .disp = ptr_mem.sib.disp + 8, |
| 11586 | 11764 | })); |
| 11587 | | const loop: u32 = @intCast(self.mir_instructions.len); |
| 11765 | const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); |
| 11588 | 11766 | const val_mem_mcv: MCValue = switch (val_mcv) { |
| 11589 | 11767 | .memory, .indirect, .load_frame => val_mcv, |
| 11590 | 11768 | else => .{ .indirect = .{ |