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