| ... | ... | @@ -33,9 +33,11 @@ const errUnionPayloadOffset = codegen.errUnionPayloadOffset; |
| 33 | 33 | const errUnionErrorOffset = codegen.errUnionErrorOffset; |
| 34 | 34 | |
| 35 | 35 | const Condition = bits.Condition; |
| 36 | const Immediate = bits.Immediate; |
| 37 | const Memory = bits.Memory; |
| 38 | const Register = bits.Register; |
| 36 | 39 | const RegisterManager = abi.RegisterManager; |
| 37 | 40 | const RegisterLock = RegisterManager.RegisterLock; |
| 38 | | const Register = bits.Register; |
| 39 | 41 | |
| 40 | 42 | const gp = abi.RegisterClass.gp; |
| 41 | 43 | const sse = abi.RegisterClass.sse; |
| ... | ... | @@ -398,47 +400,58 @@ fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 398 | 400 | return result; |
| 399 | 401 | } |
| 400 | 402 | |
| 401 | | fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct { |
| 402 | | op1: Mir.Operand = .none, |
| 403 | | op2: Mir.Operand = .none, |
| 404 | | op3: Mir.Operand = .none, |
| 405 | | op4: Mir.Operand = .none, |
| 406 | | }) !void { |
| 407 | | const ops: Mir.Inst.Ops = blk: { |
| 408 | | if (args.op1 == .none and args.op2 == .none and args.op3 == .none and args.op4 == .none) |
| 409 | | break :blk .none; |
| 410 | | |
| 411 | | if (args.op1 == .reg and args.op2 == .reg) |
| 412 | | break :blk .rr; |
| 413 | | if (args.op1 == .reg and args.op2 == .imm) switch (args.op2.imm) { |
| 414 | | .signed => break :blk .ri_s, |
| 415 | | .unsigned => break :blk .ri_u, |
| 416 | | }; |
| 417 | | if (args.op1 == .reg) |
| 418 | | break :blk .r; |
| 419 | | if (args.op1 == .imm) switch (args.op1.imm) { |
| 420 | | .signed => break :blk .imm_s, |
| 421 | | .unsigned => break :blk .imm_u, // TODO 64bits |
| 422 | | }; |
| 403 | fn asmNone(self: *Self, tag: Mir.Inst.Tag) !void { |
| 404 | _ = try self.addInst(.{ |
| 405 | .tag = tag, |
| 406 | .ops = .none, |
| 407 | .data = undefined, |
| 408 | }); |
| 409 | } |
| 423 | 410 | |
| 424 | | unreachable; |
| 425 | | }; |
| 411 | fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { |
| 412 | _ = try self.addInst(.{ |
| 413 | .tag = tag, |
| 414 | .ops = .r, |
| 415 | .data = .{ .r = reg }, |
| 416 | }); |
| 417 | } |
| 418 | |
| 419 | fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { |
| 420 | // TODO imm64 |
| 421 | const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u; |
| 426 | 422 | const data: Mir.Inst.Data = switch (ops) { |
| 427 | | .none => undefined, |
| 428 | | .imm_s => .{ .imm_s = args.op1.imm.signed }, |
| 429 | | .imm_u => .{ .imm_u = @intCast(u32, args.op1.imm.unsigned) }, |
| 430 | | .r => .{ .r = args.op1.reg }, |
| 431 | | .rr => .{ .rr = .{ |
| 432 | | .r1 = args.op1.reg, |
| 433 | | .r2 = args.op2.reg, |
| 423 | .imm_s => .{ .imm_s = imm.signed }, |
| 424 | .imm_u => .{ .imm_u = @intCast(u32, imm.unsigned) }, |
| 425 | else => unreachable, |
| 426 | }; |
| 427 | _ = try self.addInst(.{ |
| 428 | .tag = tag, |
| 429 | .ops = ops, |
| 430 | .data = data, |
| 431 | }); |
| 432 | } |
| 433 | |
| 434 | fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Register) !void { |
| 435 | _ = try self.addInst(.{ |
| 436 | .tag = tag, |
| 437 | .ops = .rr, |
| 438 | .data = .{ .rr = .{ |
| 439 | .r1 = reg1, |
| 440 | .r2 = reg2, |
| 434 | 441 | } }, |
| 442 | }); |
| 443 | } |
| 444 | |
| 445 | fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { |
| 446 | const ops: Mir.Inst.Ops = if (imm == .signed) .ri_s else .ri_u; |
| 447 | const data: Mir.Inst.Data = switch (ops) { |
| 435 | 448 | .ri_s => .{ .ri_s = .{ |
| 436 | | .r1 = args.op1.reg, |
| 437 | | .imm = args.op2.imm.signed, |
| 449 | .r1 = reg, |
| 450 | .imm = imm.signed, |
| 438 | 451 | } }, |
| 439 | 452 | .ri_u => .{ .ri_u = .{ |
| 440 | | .r1 = args.op1.reg, |
| 441 | | .imm = @intCast(u32, args.op2.imm.unsigned), |
| 453 | .r1 = reg, |
| 454 | .imm = @intCast(u32, imm.unsigned), |
| 442 | 455 | } }, |
| 443 | 456 | else => unreachable, |
| 444 | 457 | }; |
| ... | ... | @@ -452,13 +465,8 @@ fn assemble(self: *Self, tag: Mir.Inst.Tag, args: struct { |
| 452 | 465 | fn gen(self: *Self) InnerError!void { |
| 453 | 466 | const cc = self.fn_type.fnCallingConvention(); |
| 454 | 467 | if (cc != .Naked) { |
| 455 | | try self.assemble(.push, .{ |
| 456 | | .op1 = .{ .reg = .rbp }, |
| 457 | | }); |
| 458 | | try self.assemble(.mov, .{ |
| 459 | | .op1 = .{ .reg = .rbp }, |
| 460 | | .op2 = .{ .reg = .rsp }, |
| 461 | | }); |
| 468 | try self.asmRegister(.push, .rbp); |
| 469 | try self.asmRegisterRegister(.mov, .rbp, .rsp); |
| 462 | 470 | |
| 463 | 471 | // We want to subtract the aligned stack frame size from rsp here, but we don't |
| 464 | 472 | // yet know how big it will be, so we leave room for a 4-byte stack size. |
| ... | ... | @@ -541,8 +549,8 @@ fn gen(self: *Self) InnerError!void { |
| 541 | 549 | .data = undefined, |
| 542 | 550 | }); |
| 543 | 551 | |
| 544 | | try self.assemble(.pop, .{ .op1 = .{ .reg = .rbp } }); |
| 545 | | try self.assemble(.ret, .{}); |
| 552 | try self.asmRegister(.pop, .rbp); |
| 553 | try self.asmNone(.ret); |
| 546 | 554 | |
| 547 | 555 | // Adjust the stack |
| 548 | 556 | if (self.max_end_stack > math.maxInt(i32)) { |
| ... | ... | @@ -5313,23 +5321,19 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5313 | 5321 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 5314 | 5322 | while (iter.next()) |ins| { |
| 5315 | 5323 | if (mem.eql(u8, ins, "syscall")) { |
| 5316 | | try self.assemble(.syscall, .{}); |
| 5324 | try self.asmNone(.syscall); |
| 5317 | 5325 | } else if (mem.indexOf(u8, ins, "push")) |_| { |
| 5318 | 5326 | const arg = ins[4..]; |
| 5319 | 5327 | if (mem.indexOf(u8, arg, "$")) |l| { |
| 5320 | 5328 | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { |
| 5321 | 5329 | return self.fail("TODO implement more inline asm int parsing", .{}); |
| 5322 | 5330 | }; |
| 5323 | | try self.assemble(.push, .{ |
| 5324 | | .op1 = .{ .imm = Mir.Operand.Immediate.u(n) }, |
| 5325 | | }); |
| 5331 | try self.asmImmediate(.push, Immediate.u(n)); |
| 5326 | 5332 | } else if (mem.indexOf(u8, arg, "%%")) |l| { |
| 5327 | 5333 | const reg_name = ins[4 + l + 2 ..]; |
| 5328 | 5334 | const reg = parseRegName(reg_name) orelse |
| 5329 | 5335 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 5330 | | try self.assemble(.push, .{ |
| 5331 | | .op1 = .{ .reg = reg }, |
| 5332 | | }); |
| 5336 | try self.asmRegister(.push, reg); |
| 5333 | 5337 | } else return self.fail("TODO more push operands", .{}); |
| 5334 | 5338 | } else if (mem.indexOf(u8, ins, "pop")) |_| { |
| 5335 | 5339 | const arg = ins[3..]; |
| ... | ... | @@ -5337,9 +5341,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5337 | 5341 | const reg_name = ins[3 + l + 2 ..]; |
| 5338 | 5342 | const reg = parseRegName(reg_name) orelse |
| 5339 | 5343 | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 5340 | | try self.assemble(.pop, .{ |
| 5341 | | .op1 = .{ .reg = reg }, |
| 5342 | | }); |
| 5344 | try self.asmRegister(.pop, reg); |
| 5343 | 5345 | } else return self.fail("TODO more pop operands", .{}); |
| 5344 | 5346 | } else { |
| 5345 | 5347 | return self.fail("TODO implement support for more x86 assembly instructions", .{}); |
| ... | ... | @@ -6119,19 +6121,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 6119 | 6121 | // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit |
| 6120 | 6122 | // register is the fastest way to zero a register. |
| 6121 | 6123 | if (x == 0) { |
| 6122 | | try self.assemble(.xor, .{ |
| 6123 | | .op1 = .{ .reg = reg.to32() }, |
| 6124 | | .op2 = .{ .reg = reg.to32() }, |
| 6125 | | }); |
| 6126 | | return; |
| 6124 | return self.asmRegisterRegister(.xor, reg.to32(), reg.to32()); |
| 6127 | 6125 | } |
| 6128 | 6126 | if (x <= math.maxInt(i32)) { |
| 6129 | 6127 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. |
| 6130 | | try self.assemble(.mov, .{ |
| 6131 | | .op1 = .{ .reg = registerAlias(reg, abi_size) }, |
| 6132 | | .op2 = .{ .imm = Mir.Operand.Immediate.u(@intCast(u32, x)) }, |
| 6133 | | }); |
| 6134 | | return; |
| 6128 | return self.asmRegisterImmediate( |
| 6129 | .mov, |
| 6130 | registerAlias(reg, abi_size), |
| 6131 | Immediate.u(@intCast(u32, x)), |
| 6132 | ); |
| 6135 | 6133 | } |
| 6136 | 6134 | // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls |
| 6137 | 6135 | // this `movabs`, though this is officially just a different variant of the plain `mov` |