| ... | @@ -6265,13 +6265,23 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6265,13 +6265,23 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 6265 | const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | 6265 | const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 6266 | extra_i += inputs.len; | 6266 | extra_i += inputs.len; |
| 6267 | | 6267 | |
| 6268 | const dead = !is_volatile and self.liveness.isUnused(inst); | 6268 | var result: MCValue = .none; |
| 6269 | const result: MCValue = if (dead) .dead else result: { | 6269 | if (!is_volatile and self.liveness.isUnused(inst)) result = .dead else { |
| | 6270 | var args = std.StringArrayHashMap(MCValue).init(self.gpa); |
| | 6271 | try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len); |
| | 6272 | defer { |
| | 6273 | for (args.values()) |arg| switch (arg) { |
| | 6274 | .register => |reg| self.register_manager.unlockReg(.{ .register = reg }), |
| | 6275 | else => {}, |
| | 6276 | }; |
| | 6277 | args.deinit(); |
| | 6278 | } |
| | 6279 | |
| 6270 | if (outputs.len > 1) { | 6280 | if (outputs.len > 1) { |
| 6271 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | 6281 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 6272 | } | 6282 | } |
| 6273 | | 6283 | |
| 6274 | const output_constraint: ?[]const u8 = for (outputs) |output| { | 6284 | for (outputs) |output| { |
| 6275 | if (output != .none) { | 6285 | if (output != .none) { |
| 6276 | return self.fail("TODO implement codegen for non-expr asm", .{}); | 6286 | return self.fail("TODO implement codegen for non-expr asm", .{}); |
| 6277 | } | 6287 | } |
| ... | @@ -6282,8 +6292,21 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6282,8 +6292,21 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 6282 | // for the string, we still use the next u32 for the null terminator. | 6292 | // for the string, we still use the next u32 for the null terminator. |
| 6283 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; | 6293 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 6284 | | 6294 | |
| 6285 | break constraint; | 6295 | const mcv: MCValue = if (mem.eql(u8, constraint, "=r")) |
| 6286 | } else null; | 6296 | .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse |
| | 6297 | return self.fail("ran out of registers lowering inline asm", .{}) } |
| | 6298 | else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}")) |
| | 6299 | .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse |
| | 6300 | return self.fail("unrecognized register constraint: '{s}'", .{constraint}) } |
| | 6301 | else |
| | 6302 | return self.fail("unrecognized constraint: '{s}'", .{constraint}); |
| | 6303 | args.putAssumeCapacity(name, mcv); |
| | 6304 | switch (mcv) { |
| | 6305 | .register => |reg| _ = self.register_manager.lockRegAssumeUnused(reg), |
| | 6306 | else => {}, |
| | 6307 | } |
| | 6308 | if (output == .none) result = mcv; |
| | 6309 | } |
| 6287 | | 6310 | |
| 6288 | for (inputs) |input| { | 6311 | for (inputs) |input| { |
| 6289 | const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); | 6312 | const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); |
| ... | @@ -6317,52 +6340,73 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -6317,52 +6340,73 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 6317 | } | 6340 | } |
| 6318 | } | 6341 | } |
| 6319 | | 6342 | |
| 6320 | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; | 6343 | const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; |
| 6321 | | 6344 | var line_it = mem.tokenize(u8, asm_source, "\n\r"); |
| 6322 | { | 6345 | while (line_it.next()) |line| { |
| 6323 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); | 6346 | var mnem_it = mem.tokenize(u8, line, " \t"); |
| 6324 | while (iter.next()) |ins| { | 6347 | const mnem = mnem_it.next() orelse continue; |
| 6325 | if (mem.eql(u8, ins, "syscall")) { | 6348 | if (mem.startsWith(u8, mnem, "#")) continue; |
| 6326 | try self.asmOpOnly(.syscall); | 6349 | var arg_it = mem.tokenize(u8, mnem_it.rest(), ", "); |
| 6327 | } else if (mem.indexOf(u8, ins, "push")) |_| { | 6350 | if (std.ascii.eqlIgnoreCase(mnem, "syscall")) { |
| 6328 | const arg = ins[4..]; | 6351 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6329 | if (mem.indexOf(u8, arg, "$")) |l| { | 6352 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6330 | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { | 6353 | try self.asmOpOnly(.syscall); |
| 6331 | return self.fail("TODO implement more inline asm int parsing", .{}); | 6354 | } else if (std.ascii.eqlIgnoreCase(mnem, "push")) { |
| 6332 | }; | 6355 | const src = arg_it.next() orelse |
| 6333 | try self.asmImmediate(.push, Immediate.u(n)); | 6356 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6334 | } else if (mem.indexOf(u8, arg, "%%")) |l| { | 6357 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6335 | const reg_name = ins[4 + l + 2 ..]; | 6358 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6336 | const reg = parseRegName(reg_name) orelse | 6359 | if (mem.startsWith(u8, src, "$")) { |
| 6337 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 6360 | const imm = std.fmt.parseInt(u32, src["$".len..], 0) catch |
| 6338 | try self.asmRegister(.push, reg); | 6361 | return self.fail("Invalid immediate: '{s}'", .{src}); |
| 6339 | } else return self.fail("TODO more push operands", .{}); | 6362 | try self.asmImmediate(.push, Immediate.u(imm)); |
| 6340 | } else if (mem.indexOf(u8, ins, "pop")) |_| { | 6363 | } else if (mem.startsWith(u8, src, "%%")) { |
| 6341 | const arg = ins[3..]; | 6364 | const reg = parseRegName(src["%%".len..]) orelse |
| 6342 | if (mem.indexOf(u8, arg, "%%")) |l| { | 6365 | return self.fail("Invalid register: '{s}'", .{src}); |
| 6343 | const reg_name = ins[3 + l + 2 ..]; | 6366 | try self.asmRegister(.push, reg); |
| 6344 | const reg = parseRegName(reg_name) orelse | 6367 | } else return self.fail("Unsupported operand: '{s}'", .{src}); |
| 6345 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 6368 | } else if (std.ascii.eqlIgnoreCase(mnem, "pop")) { |
| 6346 | try self.asmRegister(.pop, reg); | 6369 | const dst = arg_it.next() orelse |
| 6347 | } else return self.fail("TODO more pop operands", .{}); | 6370 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6348 | } else { | 6371 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6349 | return self.fail("TODO implement support for more x86 assembly instructions", .{}); | 6372 | return self.fail("Too many operands: '{s}'", .{line}); |
| | 6373 | if (mem.startsWith(u8, dst, "%%")) { |
| | 6374 | const reg = parseRegName(dst["%%".len..]) orelse |
| | 6375 | return self.fail("Invalid register: '{s}'", .{dst}); |
| | 6376 | try self.asmRegister(.pop, reg); |
| | 6377 | } else return self.fail("Unsupported operand: '{s}'", .{dst}); |
| | 6378 | } else if (std.ascii.eqlIgnoreCase(mnem, "movq")) { |
| | 6379 | const src = arg_it.next() orelse |
| | 6380 | return self.fail("Not enough operands: '{s}'", .{line}); |
| | 6381 | const dst = arg_it.next() orelse |
| | 6382 | return self.fail("Not enough operands: '{s}'", .{line}); |
| | 6383 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| | 6384 | return self.fail("Too many operands: '{s}'", .{line}); |
| | 6385 | if (mem.startsWith(u8, src, "%%")) { |
| | 6386 | const colon = mem.indexOfScalarPos(u8, src, "%%".len + 2, ':'); |
| | 6387 | const src_reg = parseRegName(src["%%".len .. colon orelse src.len]) orelse |
| | 6388 | return self.fail("Invalid register: '{s}'", .{src}); |
| | 6389 | if (colon) |colon_pos| { |
| | 6390 | const src_disp = std.fmt.parseInt(i32, src[colon_pos + 1 ..], 0) catch |
| | 6391 | return self.fail("Invalid immediate: '{s}'", .{src}); |
| | 6392 | if (mem.startsWith(u8, dst, "%[") and mem.endsWith(u8, dst, "]")) { |
| | 6393 | switch (args.get(dst["%[".len .. dst.len - "]".len]) orelse |
| | 6394 | return self.fail("no matching constraint for: '{s}'", .{dst})) { |
| | 6395 | .register => |dst_reg| try self.asmRegisterMemory( |
| | 6396 | .mov, |
| | 6397 | dst_reg, |
| | 6398 | Memory.sib(.qword, .{ .base = src_reg, .disp = src_disp }), |
| | 6399 | ), |
| | 6400 | else => return self.fail("Invalid constraint: '{s}'", .{dst}), |
| | 6401 | } |
| | 6402 | } else return self.fail("Unsupported operand: '{s}'", .{dst}); |
| | 6403 | } else return self.fail("Unsupported operand: '{s}'", .{src}); |
| 6350 | } | 6404 | } |
| | 6405 | } else { |
| | 6406 | return self.fail("Unsupported instruction: '{s}'", .{mnem}); |
| 6351 | } | 6407 | } |
| 6352 | } | 6408 | } |
| 6353 | | 6409 | } |
| 6354 | if (output_constraint) |output| { | | |
| 6355 | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { | | |
| 6356 | return self.fail("unrecognized asm output constraint: '{s}'", .{output}); | | |
| 6357 | } | | |
| 6358 | const reg_name = output[2 .. output.len - 1]; | | |
| 6359 | const reg = parseRegName(reg_name) orelse | | |
| 6360 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | | |
| 6361 | break :result .{ .register = reg }; | | |
| 6362 | } else { | | |
| 6363 | break :result .none; | | |
| 6364 | } | | |
| 6365 | }; | | |
| 6366 | | 6410 | |
| 6367 | simple: { | 6411 | simple: { |
| 6368 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 6412 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |