| ... | @@ -5,6 +5,8 @@ const math = std.math; | ... | @@ -5,6 +5,8 @@ const math = std.math; |
| 5 | const assert = std.debug.assert; | 5 | const assert = std.debug.assert; |
| 6 | const Air = @import("../../Air.zig"); | 6 | const Air = @import("../../Air.zig"); |
| 7 | const Zir = @import("../../Zir.zig"); | 7 | const Zir = @import("../../Zir.zig"); |
| | 8 | const Mir = @import("Mir.zig"); |
| | 9 | const Emit = @import("Emit.zig"); |
| 8 | const Liveness = @import("../../Liveness.zig"); | 10 | const Liveness = @import("../../Liveness.zig"); |
| 9 | const Type = @import("../../type.zig").Type; | 11 | const Type = @import("../../type.zig").Type; |
| 10 | const Value = @import("../../value.zig").Value; | 12 | const Value = @import("../../value.zig").Value; |
| ... | @@ -31,14 +33,12 @@ const InnerError = error{ | ... | @@ -31,14 +33,12 @@ const InnerError = error{ |
| 31 | CodegenFail, | 33 | CodegenFail, |
| 32 | }; | 34 | }; |
| 33 | | 35 | |
| 34 | arch: std.Target.Cpu.Arch, | | |
| 35 | gpa: *Allocator, | 36 | gpa: *Allocator, |
| 36 | air: Air, | 37 | air: Air, |
| 37 | liveness: Liveness, | 38 | liveness: Liveness, |
| 38 | bin_file: *link.File, | 39 | bin_file: *link.File, |
| 39 | target: *const std.Target, | 40 | target: *const std.Target, |
| 40 | mod_fn: *const Module.Fn, | 41 | mod_fn: *const Module.Fn, |
| 41 | code: *std.ArrayList(u8), | | |
| 42 | debug_output: DebugInfoOutput, | 42 | debug_output: DebugInfoOutput, |
| 43 | err_msg: ?*ErrorMsg, | 43 | err_msg: ?*ErrorMsg, |
| 44 | args: []MCValue, | 44 | args: []MCValue, |
| ... | @@ -48,6 +48,11 @@ arg_index: usize, | ... | @@ -48,6 +48,11 @@ arg_index: usize, |
| 48 | src_loc: Module.SrcLoc, | 48 | src_loc: Module.SrcLoc, |
| 49 | stack_align: u32, | 49 | stack_align: u32, |
| 50 | | 50 | |
| | 51 | /// MIR Instructions |
| | 52 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| | 53 | /// MIR extra data |
| | 54 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| | 55 | |
| 51 | prev_di_line: u32, | 56 | prev_di_line: u32, |
| 52 | prev_di_column: u32, | 57 | prev_di_column: u32, |
| 53 | /// Byte offset within the source file of the ending curly. | 58 | /// Byte offset within the source file of the ending curly. |
| ... | @@ -237,7 +242,6 @@ const BigTomb = struct { | ... | @@ -237,7 +242,6 @@ const BigTomb = struct { |
| 237 | const Self = @This(); | 242 | const Self = @This(); |
| 238 | | 243 | |
| 239 | pub fn generate( | 244 | pub fn generate( |
| 240 | arch: std.Target.Cpu.Arch, | | |
| 241 | bin_file: *link.File, | 245 | bin_file: *link.File, |
| 242 | src_loc: Module.SrcLoc, | 246 | src_loc: Module.SrcLoc, |
| 243 | module_fn: *Module.Fn, | 247 | module_fn: *Module.Fn, |
| ... | @@ -246,7 +250,7 @@ pub fn generate( | ... | @@ -246,7 +250,7 @@ pub fn generate( |
| 246 | code: *std.ArrayList(u8), | 250 | code: *std.ArrayList(u8), |
| 247 | debug_output: DebugInfoOutput, | 251 | debug_output: DebugInfoOutput, |
| 248 | ) GenerateSymbolError!FnResult { | 252 | ) GenerateSymbolError!FnResult { |
| 249 | if (build_options.skip_non_native and builtin.cpu.arch != arch) { | 253 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 250 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 254 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 251 | } | 255 | } |
| 252 | | 256 | |
| ... | @@ -262,14 +266,12 @@ pub fn generate( | ... | @@ -262,14 +266,12 @@ pub fn generate( |
| 262 | try branch_stack.append(.{}); | 266 | try branch_stack.append(.{}); |
| 263 | | 267 | |
| 264 | var function = Self{ | 268 | var function = Self{ |
| 265 | .arch = arch, | | |
| 266 | .gpa = bin_file.allocator, | 269 | .gpa = bin_file.allocator, |
| 267 | .air = air, | 270 | .air = air, |
| 268 | .liveness = liveness, | 271 | .liveness = liveness, |
| 269 | .target = &bin_file.options.target, | 272 | .target = &bin_file.options.target, |
| 270 | .bin_file = bin_file, | 273 | .bin_file = bin_file, |
| 271 | .mod_fn = module_fn, | 274 | .mod_fn = module_fn, |
| 272 | .code = code, | | |
| 273 | .debug_output = debug_output, | 275 | .debug_output = debug_output, |
| 274 | .err_msg = null, | 276 | .err_msg = null, |
| 275 | .args = undefined, // populated after `resolveCallingConventionValues` | 277 | .args = undefined, // populated after `resolveCallingConventionValues` |
| ... | @@ -305,6 +307,19 @@ pub fn generate( | ... | @@ -305,6 +307,19 @@ pub fn generate( |
| 305 | else => |e| return e, | 307 | else => |e| return e, |
| 306 | }; | 308 | }; |
| 307 | | 309 | |
| | 310 | var mir = Mir{ |
| | 311 | .instructions = function.mir_instructions.toOwnedSlice(), |
| | 312 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), |
| | 313 | }; |
| | 314 | defer mir.deinit(bin_file.allocator); |
| | 315 | |
| | 316 | var emit = Emit{ |
| | 317 | .mir = mir, |
| | 318 | .target = &bin_file.options.target, |
| | 319 | .code = code, |
| | 320 | }; |
| | 321 | try emit.emitMir(); |
| | 322 | |
| 308 | if (function.err_msg) |em| { | 323 | if (function.err_msg) |em| { |
| 309 | return FnResult{ .fail = em }; | 324 | return FnResult{ .fail = em }; |
| 310 | } else { | 325 | } else { |
| ... | @@ -312,6 +327,16 @@ pub fn generate( | ... | @@ -312,6 +327,16 @@ pub fn generate( |
| 312 | } | 327 | } |
| 313 | } | 328 | } |
| 314 | | 329 | |
| | 330 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| | 331 | const gpa = self.gpa; |
| | 332 | |
| | 333 | try self.mir_instructions.ensureUnusedCapacity(gpa, 1); |
| | 334 | |
| | 335 | const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); |
| | 336 | self.mir_instructions.appendAssumeCapacity(inst); |
| | 337 | return result_index; |
| | 338 | } |
| | 339 | |
| 315 | fn gen(self: *Self) !void { | 340 | fn gen(self: *Self) !void { |
| 316 | const cc = self.fn_type.fnCallingConvention(); | 341 | const cc = self.fn_type.fnCallingConvention(); |
| 317 | if (cc != .Naked) { | 342 | if (cc != .Naked) { |
| ... | @@ -320,15 +345,26 @@ fn gen(self: *Self) !void { | ... | @@ -320,15 +345,26 @@ fn gen(self: *Self) !void { |
| 320 | // stp fp, lr, [sp, #-16]! | 345 | // stp fp, lr, [sp, #-16]! |
| 321 | // mov fp, sp | 346 | // mov fp, sp |
| 322 | // sub sp, sp, #reloc | 347 | // sub sp, sp, #reloc |
| 323 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp( | 348 | |
| 324 | .x29, | 349 | _ = try self.addInst(.{ |
| 325 | .x30, | 350 | .tag = .stp, |
| 326 | Register.sp, | 351 | .data = .{ .load_store_register_pair = .{ |
| 327 | Instruction.LoadStorePairOffset.pre_index(-16), | 352 | .rt = .x29, |
| 328 | ).toU32()); | 353 | .rt2 = .x30, |
| 329 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32()); | 354 | .rn = Register.sp, |
| 330 | const backpatch_reloc = self.code.items.len; | 355 | .offset = Instruction.LoadStorePairOffset.pre_index(-16), |
| 331 | try self.code.resize(backpatch_reloc + 4); | 356 | } }, |
| | 357 | }); |
| | 358 | |
| | 359 | _ = try self.addInst(.{ |
| | 360 | .tag = .mov_to_from_sp, |
| | 361 | .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } }, |
| | 362 | }); |
| | 363 | |
| | 364 | const backpatch_reloc = try self.addInst(.{ |
| | 365 | .tag = .nop, |
| | 366 | .data = .{ .nop = {} }, |
| | 367 | }); |
| 332 | | 368 | |
| 333 | try self.dbgSetPrologueEnd(); | 369 | try self.dbgSetPrologueEnd(); |
| 334 | | 370 | |
| ... | @@ -338,7 +374,10 @@ fn gen(self: *Self) !void { | ... | @@ -338,7 +374,10 @@ fn gen(self: *Self) !void { |
| 338 | const stack_end = self.max_end_stack; | 374 | const stack_end = self.max_end_stack; |
| 339 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); | 375 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 340 | if (math.cast(u12, aligned_stack_end)) |size| { | 376 | if (math.cast(u12, aligned_stack_end)) |size| { |
| 341 | mem.writeIntLittle(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.xzr, .xzr, size, false).toU32()); | 377 | self.mir_instructions.set(backpatch_reloc, .{ |
| | 378 | .tag = .sub_immediate, |
| | 379 | .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } }, |
| | 380 | }); |
| 342 | } else |_| { | 381 | } else |_| { |
| 343 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); | 382 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); |
| 344 | } | 383 | } |
| ... | @@ -352,36 +391,36 @@ fn gen(self: *Self) !void { | ... | @@ -352,36 +391,36 @@ fn gen(self: *Self) !void { |
| 352 | // the code. Therefore, we can just delete | 391 | // the code. Therefore, we can just delete |
| 353 | // the space initially reserved for the | 392 | // the space initially reserved for the |
| 354 | // jump | 393 | // jump |
| 355 | self.code.items.len -= 4; | 394 | self.mir_instructions.len -= 1; |
| 356 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { | 395 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 357 | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8); | 396 | self.mir_instructions.set(jmp_reloc, .{ |
| 358 | if (amt == -4) { | 397 | .tag = .b, |
| 359 | // This return is at the end of the | 398 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, |
| 360 | // code block. We can't just delete | 399 | }); |
| 361 | // the space because there may be | | |
| 362 | // other jumps we already relocated to | | |
| 363 | // the address. Instead, insert a nop | | |
| 364 | mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32()); | | |
| 365 | } else { | | |
| 366 | if (math.cast(i28, amt)) |offset| { | | |
| 367 | mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(offset).toU32()); | | |
| 368 | } else |_| { | | |
| 369 | return self.failSymbol("exitlude jump is too large", .{}); | | |
| 370 | } | | |
| 371 | } | | |
| 372 | } | 400 | } |
| 373 | | 401 | |
| 374 | // ldp fp, lr, [sp], #16 | 402 | // ldp fp, lr, [sp], #16 |
| 375 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp( | 403 | _ = try self.addInst(.{ |
| 376 | .x29, | 404 | .tag = .ldp, |
| 377 | .x30, | 405 | .data = .{ .load_store_register_pair = .{ |
| 378 | Register.sp, | 406 | .rt = .x29, |
| 379 | Instruction.LoadStorePairOffset.post_index(16), | 407 | .rt2 = .x30, |
| 380 | ).toU32()); | 408 | .rn = Register.sp, |
| | 409 | .offset = Instruction.LoadStorePairOffset.post_index(16), |
| | 410 | } }, |
| | 411 | }); |
| | 412 | |
| 381 | // add sp, sp, #stack_size | 413 | // add sp, sp, #stack_size |
| 382 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.xzr, .xzr, @intCast(u12, aligned_stack_end), false).toU32()); | 414 | _ = try self.addInst(.{ |
| | 415 | .tag = .add_immediate, |
| | 416 | .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, aligned_stack_end) } }, |
| | 417 | }); |
| | 418 | |
| 383 | // ret lr | 419 | // ret lr |
| 384 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32()); | 420 | _ = try self.addInst(.{ |
| | 421 | .tag = .ret, |
| | 422 | .data = .{ .reg = .x30 }, |
| | 423 | }); |
| 385 | } else { | 424 | } else { |
| 386 | try self.dbgSetPrologueEnd(); | 425 | try self.dbgSetPrologueEnd(); |
| 387 | try self.genBody(self.air.getMainBody()); | 426 | try self.genBody(self.air.getMainBody()); |
| ... | @@ -389,7 +428,7 @@ fn gen(self: *Self) !void { | ... | @@ -389,7 +428,7 @@ fn gen(self: *Self) !void { |
| 389 | } | 428 | } |
| 390 | | 429 | |
| 391 | // Drop them off at the rbrace. | 430 | // Drop them off at the rbrace. |
| 392 | try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column); | 431 | // try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column); |
| 393 | } | 432 | } |
| 394 | | 433 | |
| 395 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | 434 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| ... | @@ -534,7 +573,7 @@ fn dbgSetPrologueEnd(self: *Self) InnerError!void { | ... | @@ -534,7 +573,7 @@ fn dbgSetPrologueEnd(self: *Self) InnerError!void { |
| 534 | switch (self.debug_output) { | 573 | switch (self.debug_output) { |
| 535 | .dwarf => |dbg_out| { | 574 | .dwarf => |dbg_out| { |
| 536 | try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); | 575 | try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); |
| 537 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); | 576 | // try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 538 | }, | 577 | }, |
| 539 | .plan9 => {}, | 578 | .plan9 => {}, |
| 540 | .none => {}, | 579 | .none => {}, |
| ... | @@ -545,7 +584,7 @@ fn dbgSetEpilogueBegin(self: *Self) InnerError!void { | ... | @@ -545,7 +584,7 @@ fn dbgSetEpilogueBegin(self: *Self) InnerError!void { |
| 545 | switch (self.debug_output) { | 584 | switch (self.debug_output) { |
| 546 | .dwarf => |dbg_out| { | 585 | .dwarf => |dbg_out| { |
| 547 | try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); | 586 | try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); |
| 548 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); | 587 | // try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 549 | }, | 588 | }, |
| 550 | .plan9 => {}, | 589 | .plan9 => {}, |
| 551 | .none => {}, | 590 | .none => {}, |
| ... | @@ -1297,310 +1336,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1297,310 +1336,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1297 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); | 1336 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 1298 | } | 1337 | } |
| 1299 | | 1338 | |
| 1300 | fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { | | |
| 1301 | return switch (mcv) { | | |
| 1302 | .none => unreachable, | | |
| 1303 | .undef => unreachable, | | |
| 1304 | .dead, .unreach => unreachable, | | |
| 1305 | .compare_flags_unsigned => unreachable, | | |
| 1306 | .compare_flags_signed => unreachable, | | |
| 1307 | .ptr_stack_offset => unreachable, | | |
| 1308 | .ptr_embedded_in_code => unreachable, | | |
| 1309 | .immediate => |imm| blk: { | | |
| 1310 | if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{}); | | |
| 1311 | | | |
| 1312 | // Load immediate into register if it doesn't fit | | |
| 1313 | // in an operand | | |
| 1314 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null; | | |
| 1315 | }, | | |
| 1316 | .register => true, | | |
| 1317 | .stack_offset, | | |
| 1318 | .embedded_in_code, | | |
| 1319 | .memory, | | |
| 1320 | => true, | | |
| 1321 | }; | | |
| 1322 | } | | |
| 1323 | | | |
| 1324 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { | | |
| 1325 | // In the case of bitshifts, the type of rhs is different | | |
| 1326 | // from the resulting type | | |
| 1327 | const ty = self.air.typeOf(op_lhs); | | |
| 1328 | | | |
| 1329 | switch (ty.zigTypeTag()) { | | |
| 1330 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | | |
| 1331 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | | |
| 1332 | .Bool => { | | |
| 1333 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); | | |
| 1334 | }, | | |
| 1335 | .Int => { | | |
| 1336 | const int_info = ty.intInfo(self.target.*); | | |
| 1337 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); | | |
| 1338 | }, | | |
| 1339 | else => unreachable, | | |
| 1340 | } | | |
| 1341 | } | | |
| 1342 | | | |
| 1343 | fn genArmBinIntOp( | | |
| 1344 | self: *Self, | | |
| 1345 | inst: Air.Inst.Index, | | |
| 1346 | op_lhs: Air.Inst.Ref, | | |
| 1347 | op_rhs: Air.Inst.Ref, | | |
| 1348 | op: Air.Inst.Tag, | | |
| 1349 | bits: u16, | | |
| 1350 | signedness: std.builtin.Signedness, | | |
| 1351 | ) !MCValue { | | |
| 1352 | if (bits > 32) { | | |
| 1353 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | | |
| 1354 | } | | |
| 1355 | | | |
| 1356 | const lhs = try self.resolveInst(op_lhs); | | |
| 1357 | const rhs = try self.resolveInst(op_rhs); | | |
| 1358 | | | |
| 1359 | const lhs_is_register = lhs == .register; | | |
| 1360 | const rhs_is_register = rhs == .register; | | |
| 1361 | const lhs_should_be_register = switch (op) { | | |
| 1362 | .shr, .shl => true, | | |
| 1363 | else => try self.armOperandShouldBeRegister(lhs), | | |
| 1364 | }; | | |
| 1365 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); | | |
| 1366 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); | | |
| 1367 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); | | |
| 1368 | const can_swap_lhs_and_rhs = switch (op) { | | |
| 1369 | .shr, .shl => false, | | |
| 1370 | else => true, | | |
| 1371 | }; | | |
| 1372 | | | |
| 1373 | // Destination must be a register | | |
| 1374 | var dst_mcv: MCValue = undefined; | | |
| 1375 | var lhs_mcv = lhs; | | |
| 1376 | var rhs_mcv = rhs; | | |
| 1377 | var swap_lhs_and_rhs = false; | | |
| 1378 | | | |
| 1379 | // Allocate registers for operands and/or destination | | |
| 1380 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 1381 | if (reuse_lhs) { | | |
| 1382 | // Allocate 0 or 1 registers | | |
| 1383 | if (!rhs_is_register and rhs_should_be_register) { | | |
| 1384 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) }; | | |
| 1385 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | | |
| 1386 | } | | |
| 1387 | dst_mcv = lhs; | | |
| 1388 | } else if (reuse_rhs and can_swap_lhs_and_rhs) { | | |
| 1389 | // Allocate 0 or 1 registers | | |
| 1390 | if (!lhs_is_register and lhs_should_be_register) { | | |
| 1391 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; | | |
| 1392 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); | | |
| 1393 | } | | |
| 1394 | dst_mcv = rhs; | | |
| 1395 | | | |
| 1396 | swap_lhs_and_rhs = true; | | |
| 1397 | } else { | | |
| 1398 | // Allocate 1 or 2 registers | | |
| 1399 | if (lhs_should_be_register and rhs_should_be_register) { | | |
| 1400 | if (lhs_is_register and rhs_is_register) { | | |
| 1401 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) }; | | |
| 1402 | } else if (lhs_is_register) { | | |
| 1403 | // Move RHS to register | | |
| 1404 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; | | |
| 1405 | rhs_mcv = dst_mcv; | | |
| 1406 | } else if (rhs_is_register) { | | |
| 1407 | // Move LHS to register | | |
| 1408 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; | | |
| 1409 | lhs_mcv = dst_mcv; | | |
| 1410 | } else { | | |
| 1411 | // Move LHS and RHS to register | | |
| 1412 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{}); | | |
| 1413 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 1414 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 1415 | dst_mcv = lhs_mcv; | | |
| 1416 | | | |
| 1417 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | | |
| 1418 | } | | |
| 1419 | } else if (lhs_should_be_register) { | | |
| 1420 | // RHS is immediate | | |
| 1421 | if (lhs_is_register) { | | |
| 1422 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; | | |
| 1423 | } else { | | |
| 1424 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; | | |
| 1425 | lhs_mcv = dst_mcv; | | |
| 1426 | } | | |
| 1427 | } else if (rhs_should_be_register and can_swap_lhs_and_rhs) { | | |
| 1428 | // LHS is immediate | | |
| 1429 | if (rhs_is_register) { | | |
| 1430 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; | | |
| 1431 | } else { | | |
| 1432 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; | | |
| 1433 | rhs_mcv = dst_mcv; | | |
| 1434 | } | | |
| 1435 | | | |
| 1436 | swap_lhs_and_rhs = true; | | |
| 1437 | } else unreachable; // binary operation on two immediates | | |
| 1438 | } | | |
| 1439 | | | |
| 1440 | // Move the operands to the newly allocated registers | | |
| 1441 | if (lhs_mcv == .register and !lhs_is_register) { | | |
| 1442 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | | |
| 1443 | } | | |
| 1444 | if (rhs_mcv == .register and !rhs_is_register) { | | |
| 1445 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | | |
| 1446 | } | | |
| 1447 | | | |
| 1448 | try self.genArmBinOpCode( | | |
| 1449 | dst_mcv.register, | | |
| 1450 | lhs_mcv, | | |
| 1451 | rhs_mcv, | | |
| 1452 | swap_lhs_and_rhs, | | |
| 1453 | op, | | |
| 1454 | signedness, | | |
| 1455 | ); | | |
| 1456 | return dst_mcv; | | |
| 1457 | } | | |
| 1458 | | | |
| 1459 | fn genArmBinOpCode( | | |
| 1460 | self: *Self, | | |
| 1461 | dst_reg: Register, | | |
| 1462 | lhs_mcv: MCValue, | | |
| 1463 | rhs_mcv: MCValue, | | |
| 1464 | swap_lhs_and_rhs: bool, | | |
| 1465 | op: Air.Inst.Tag, | | |
| 1466 | signedness: std.builtin.Signedness, | | |
| 1467 | ) !void { | | |
| 1468 | assert(lhs_mcv == .register or rhs_mcv == .register); | | |
| 1469 | | | |
| 1470 | const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register; | | |
| 1471 | const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv; | | |
| 1472 | | | |
| 1473 | const operand = switch (op2) { | | |
| 1474 | .none => unreachable, | | |
| 1475 | .undef => unreachable, | | |
| 1476 | .dead, .unreach => unreachable, | | |
| 1477 | .compare_flags_unsigned => unreachable, | | |
| 1478 | .compare_flags_signed => unreachable, | | |
| 1479 | .ptr_stack_offset => unreachable, | | |
| 1480 | .ptr_embedded_in_code => unreachable, | | |
| 1481 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, | | |
| 1482 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), | | |
| 1483 | .stack_offset, | | |
| 1484 | .embedded_in_code, | | |
| 1485 | .memory, | | |
| 1486 | => unreachable, | | |
| 1487 | }; | | |
| 1488 | | | |
| 1489 | switch (op) { | | |
| 1490 | .add => { | | |
| 1491 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32()); | | |
| 1492 | }, | | |
| 1493 | .sub => { | | |
| 1494 | if (swap_lhs_and_rhs) { | | |
| 1495 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32()); | | |
| 1496 | } else { | | |
| 1497 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32()); | | |
| 1498 | } | | |
| 1499 | }, | | |
| 1500 | .bool_and, .bit_and => { | | |
| 1501 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32()); | | |
| 1502 | }, | | |
| 1503 | .bool_or, .bit_or => { | | |
| 1504 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32()); | | |
| 1505 | }, | | |
| 1506 | .not, .xor => { | | |
| 1507 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32()); | | |
| 1508 | }, | | |
| 1509 | .cmp_eq => { | | |
| 1510 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32()); | | |
| 1511 | }, | | |
| 1512 | .shl => { | | |
| 1513 | assert(!swap_lhs_and_rhs); | | |
| 1514 | const shift_amount = switch (operand) { | | |
| 1515 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | | |
| 1516 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | | |
| 1517 | }; | | |
| 1518 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32()); | | |
| 1519 | }, | | |
| 1520 | .shr => { | | |
| 1521 | assert(!swap_lhs_and_rhs); | | |
| 1522 | const shift_amount = switch (operand) { | | |
| 1523 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | | |
| 1524 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | | |
| 1525 | }; | | |
| 1526 | | | |
| 1527 | const shr = switch (signedness) { | | |
| 1528 | .signed => Instruction.asr, | | |
| 1529 | .unsigned => Instruction.lsr, | | |
| 1530 | }; | | |
| 1531 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32()); | | |
| 1532 | }, | | |
| 1533 | else => unreachable, // not a binary instruction | | |
| 1534 | } | | |
| 1535 | } | | |
| 1536 | | | |
| 1537 | fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue { | | |
| 1538 | const lhs = try self.resolveInst(op_lhs); | | |
| 1539 | const rhs = try self.resolveInst(op_rhs); | | |
| 1540 | | | |
| 1541 | const lhs_is_register = lhs == .register; | | |
| 1542 | const rhs_is_register = rhs == .register; | | |
| 1543 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); | | |
| 1544 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); | | |
| 1545 | | | |
| 1546 | // Destination must be a register | | |
| 1547 | // LHS must be a register | | |
| 1548 | // RHS must be a register | | |
| 1549 | var dst_mcv: MCValue = undefined; | | |
| 1550 | var lhs_mcv: MCValue = lhs; | | |
| 1551 | var rhs_mcv: MCValue = rhs; | | |
| 1552 | | | |
| 1553 | // Allocate registers for operands and/or destination | | |
| 1554 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 1555 | if (reuse_lhs) { | | |
| 1556 | // Allocate 0 or 1 registers | | |
| 1557 | if (!rhs_is_register) { | | |
| 1558 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) }; | | |
| 1559 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | | |
| 1560 | } | | |
| 1561 | dst_mcv = lhs; | | |
| 1562 | } else if (reuse_rhs) { | | |
| 1563 | // Allocate 0 or 1 registers | | |
| 1564 | if (!lhs_is_register) { | | |
| 1565 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; | | |
| 1566 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv); | | |
| 1567 | } | | |
| 1568 | dst_mcv = rhs; | | |
| 1569 | } else { | | |
| 1570 | // Allocate 1 or 2 registers | | |
| 1571 | if (lhs_is_register and rhs_is_register) { | | |
| 1572 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) }; | | |
| 1573 | } else if (lhs_is_register) { | | |
| 1574 | // Move RHS to register | | |
| 1575 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) }; | | |
| 1576 | rhs_mcv = dst_mcv; | | |
| 1577 | } else if (rhs_is_register) { | | |
| 1578 | // Move LHS to register | | |
| 1579 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; | | |
| 1580 | lhs_mcv = dst_mcv; | | |
| 1581 | } else { | | |
| 1582 | // Move LHS and RHS to register | | |
| 1583 | const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{}); | | |
| 1584 | lhs_mcv = MCValue{ .register = regs[0] }; | | |
| 1585 | rhs_mcv = MCValue{ .register = regs[1] }; | | |
| 1586 | dst_mcv = lhs_mcv; | | |
| 1587 | | | |
| 1588 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | | |
| 1589 | } | | |
| 1590 | } | | |
| 1591 | | | |
| 1592 | // Move the operands to the newly allocated registers | | |
| 1593 | if (!lhs_is_register) { | | |
| 1594 | try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs); | | |
| 1595 | } | | |
| 1596 | if (!rhs_is_register) { | | |
| 1597 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | | |
| 1598 | } | | |
| 1599 | | | |
| 1600 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); | | |
| 1601 | return dst_mcv; | | |
| 1602 | } | | |
| 1603 | | | |
| 1604 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { | 1339 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1605 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; | 1340 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; |
| 1606 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; | 1341 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; |
| ... | @@ -1668,7 +1403,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1668,7 +1403,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1668 | } | 1403 | } |
| 1669 | | 1404 | |
| 1670 | fn airBreakpoint(self: *Self) !void { | 1405 | fn airBreakpoint(self: *Self) !void { |
| 1671 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32()); | 1406 | _ = try self.addInst(.{ |
| | 1407 | .tag = .brk, |
| | 1408 | .data = .{ .imm16 = 1 }, |
| | 1409 | }); |
| 1672 | return self.finishAirBookkeeping(); | 1410 | return self.finishAirBookkeeping(); |
| 1673 | } | 1411 | } |
| 1674 | | 1412 | |
| ... | @@ -1736,7 +1474,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1736,7 +1474,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1736 | | 1474 | |
| 1737 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); | 1475 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 1738 | | 1476 | |
| 1739 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); | 1477 | _ = try self.addInst(.{ |
| | 1478 | .tag = .blr, |
| | 1479 | .data = .{ .reg = .x30 }, |
| | 1480 | }); |
| 1740 | } else if (func_value.castTag(.extern_fn)) |_| { | 1481 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1741 | return self.fail("TODO implement calling extern functions", .{}); | 1482 | return self.fail("TODO implement calling extern functions", .{}); |
| 1742 | } else { | 1483 | } else { |
| ... | @@ -1789,14 +1530,22 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1789,14 +1530,22 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1789 | .memory = func.owner_decl.link.macho.local_sym_index, | 1530 | .memory = func.owner_decl.link.macho.local_sym_index, |
| 1790 | }); | 1531 | }); |
| 1791 | // blr x30 | 1532 | // blr x30 |
| 1792 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); | 1533 | _ = try self.addInst(.{ |
| | 1534 | .tag = .blr, |
| | 1535 | .data = .{ .reg = .x30 }, |
| | 1536 | }); |
| 1793 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | 1537 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 1794 | const decl = func_payload.data; | 1538 | const decl = func_payload.data; |
| 1795 | const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name)); | 1539 | const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name)); |
| 1796 | const offset = blk: { | 1540 | const offset = blk: { |
| 1797 | const offset = @intCast(u32, self.code.items.len); | 1541 | // TODO add a pseudo-instruction |
| | 1542 | const offset = @intCast(u32, self.mir_instructions.len); |
| 1798 | // bl | 1543 | // bl |
| 1799 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); | 1544 | // mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); |
| | 1545 | _ = try self.addInst(.{ |
| | 1546 | .tag = .bl, |
| | 1547 | .data = .{ .nop = {} }, |
| | 1548 | }); |
| 1800 | break :blk offset; | 1549 | break :blk offset; |
| 1801 | }; | 1550 | }; |
| 1802 | // Add relocation to the decl. | 1551 | // Add relocation to the decl. |
| ... | @@ -1857,7 +1606,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1857,7 +1606,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1857 | | 1606 | |
| 1858 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); | 1607 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); |
| 1859 | | 1608 | |
| 1860 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); | 1609 | _ = try self.addInst(.{ |
| | 1610 | .tag = .blr, |
| | 1611 | .data = .{ .reg = .x30 }, |
| | 1612 | }); |
| 1861 | } else if (func_value.castTag(.extern_fn)) |_| { | 1613 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1862 | return self.fail("TODO implement calling extern functions", .{}); | 1614 | return self.fail("TODO implement calling extern functions", .{}); |
| 1863 | } else { | 1615 | } else { |
| ... | @@ -1899,8 +1651,11 @@ fn ret(self: *Self, mcv: MCValue) !void { | ... | @@ -1899,8 +1651,11 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 1899 | const ret_ty = self.fn_type.fnReturnType(); | 1651 | const ret_ty = self.fn_type.fnReturnType(); |
| 1900 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); | 1652 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1901 | // Just add space for an instruction, patch this later | 1653 | // Just add space for an instruction, patch this later |
| 1902 | try self.code.resize(self.code.items.len + 4); | 1654 | const index = try self.addInst(.{ |
| 1903 | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); | 1655 | .tag = .nop, |
| | 1656 | .data = .{ .nop = {} }, |
| | 1657 | }); |
| | 1658 | try self.exitlude_jump_relocs.append(self.gpa, index); |
| 1904 | } | 1659 | } |
| 1905 | | 1660 | |
| 1906 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 1661 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1939,7 +1694,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -1939,7 +1694,8 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1939 | | 1694 | |
| 1940 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | 1695 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1941 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; | 1696 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 1942 | try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column); | 1697 | _ = dbg_stmt; |
| | 1698 | // try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column); |
| 1943 | return self.finishAirBookkeeping(); | 1699 | return self.finishAirBookkeeping(); |
| 1944 | } | 1700 | } |
| 1945 | | 1701 | |
| ... | @@ -2090,19 +1846,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2090,19 +1846,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 2090 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1846 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2091 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | 1847 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2092 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; | 1848 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 2093 | const start_index = self.code.items.len; | 1849 | const start_index = @intCast(u32, self.mir_instructions.len); |
| 2094 | try self.genBody(body); | 1850 | try self.genBody(body); |
| 2095 | try self.jump(start_index); | 1851 | try self.jump(start_index); |
| 2096 | return self.finishAirBookkeeping(); | 1852 | return self.finishAirBookkeeping(); |
| 2097 | } | 1853 | } |
| 2098 | | 1854 | |
| 2099 | /// Send control flow to the `index` of `self.code`. | 1855 | /// Send control flow to `inst`. |
| 2100 | fn jump(self: *Self, index: usize) !void { | 1856 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { |
| 2101 | if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { | 1857 | _ = try self.addInst(.{ |
| 2102 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32()); | 1858 | .tag = .b, |
| 2103 | } else |_| { | 1859 | .data = .{ .inst = inst }, |
| 2104 | return self.fail("TODO: enable larger branch offset", .{}); | 1860 | }); |
| 2105 | } | | |
| 2106 | } | 1861 | } |
| 2107 | | 1862 | |
| 2108 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | 1863 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2140,19 +1895,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2140,19 +1895,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2140 | | 1895 | |
| 2141 | fn performReloc(self: *Self, reloc: Reloc) !void { | 1896 | fn performReloc(self: *Self, reloc: Reloc) !void { |
| 2142 | switch (reloc) { | 1897 | switch (reloc) { |
| 2143 | .rel32 => |pos| { | 1898 | .rel32 => return self.fail("TODO reloc.rel32 for {}", .{self.target.cpu.arch}), |
| 2144 | const amt = self.code.items.len - (pos + 4); | 1899 | .arm_branch => return self.fail("TODO reloc.arm_branch for {}", .{self.target.cpu.arch}), |
| 2145 | // Here it would be tempting to implement testing for amt == 0 and then elide the | | |
| 2146 | // jump. However, that will cause a problem because other jumps may assume that they | | |
| 2147 | // can jump to this code. Or maybe I didn't understand something when I was debugging. | | |
| 2148 | // It could be worth another look. Anyway, that's why that isn't done here. Probably the | | |
| 2149 | // best place to elide jumps will be in semantic analysis, by inlining blocks that only | | |
| 2150 | // only have 1 break instruction. | | |
| 2151 | const s32_amt = math.cast(i32, amt) catch | | |
| 2152 | return self.fail("unable to perform relocation: jump too far", .{}); | | |
| 2153 | mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); | | |
| 2154 | }, | | |
| 2155 | .arm_branch => unreachable, | | |
| 2156 | } | 1900 | } |
| 2157 | } | 1901 | } |
| 2158 | | 1902 | |
| ... | @@ -2244,9 +1988,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2244,9 +1988,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2244 | } | 1988 | } |
| 2245 | | 1989 | |
| 2246 | if (mem.eql(u8, asm_source, "svc #0")) { | 1990 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2247 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32()); | 1991 | _ = try self.addInst(.{ |
| | 1992 | .tag = .svc, |
| | 1993 | .data = .{ .imm16 = 0x0 }, |
| | 1994 | }); |
| 2248 | } else if (mem.eql(u8, asm_source, "svc #0x80")) { | 1995 | } else if (mem.eql(u8, asm_source, "svc #0x80")) { |
| 2249 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32()); | 1996 | _ = try self.addInst(.{ |
| | 1997 | .tag = .svc, |
| | 1998 | .data = .{ .imm16 = 0x80 }, |
| | 1999 | }); |
| 2250 | } else { | 2000 | } else { |
| 2251 | return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); | 2001 | return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); |
| 2252 | } | 2002 | } |
| ... | @@ -2333,6 +2083,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -2333,6 +2083,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2333 | return self.fail("TODO implement set stack variable from embedded_in_code", .{}); | 2083 | return self.fail("TODO implement set stack variable from embedded_in_code", .{}); |
| 2334 | }, | 2084 | }, |
| 2335 | .register => |reg| { | 2085 | .register => |reg| { |
| | 2086 | _ = reg; |
| | 2087 | |
| 2336 | const abi_size = ty.abiSize(self.target.*); | 2088 | const abi_size = ty.abiSize(self.target.*); |
| 2337 | const adj_off = stack_offset + abi_size; | 2089 | const adj_off = stack_offset + abi_size; |
| 2338 | | 2090 | |
| ... | @@ -2347,16 +2099,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -2347,16 +2099,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2347 | .aarch64_32 => .w29, | 2099 | .aarch64_32 => .w29, |
| 2348 | else => unreachable, | 2100 | else => unreachable, |
| 2349 | }; | 2101 | }; |
| 2350 | const str = switch (abi_size) { | 2102 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2351 | 1 => Instruction.strb, | 2103 | 1 => .strb, |
| 2352 | 2 => Instruction.strh, | 2104 | 2 => .strh, |
| 2353 | 4, 8 => Instruction.str, | 2105 | 4, 8 => .str, |
| 2354 | else => unreachable, // unexpected abi size | 2106 | else => unreachable, // unexpected abi size |
| 2355 | }; | 2107 | }; |
| 2356 | | 2108 | |
| 2357 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{ | 2109 | _ = try self.addInst(.{ |
| 2358 | .offset = offset, | 2110 | .tag = tag, |
| 2359 | }).toU32()); | 2111 | .data = .{ .load_store_register = .{ |
| | 2112 | .rt = reg, |
| | 2113 | .rn = rn, |
| | 2114 | .offset = offset, |
| | 2115 | } }, |
| | 2116 | }); |
| 2360 | }, | 2117 | }, |
| 2361 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 2118 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 2362 | } | 2119 | } |
| ... | @@ -2392,20 +2149,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2392,20 +2149,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2392 | } | 2149 | } |
| 2393 | }, | 2150 | }, |
| 2394 | .immediate => |x| { | 2151 | .immediate => |x| { |
| 2395 | if (x <= math.maxInt(u16)) { | 2152 | _ = try self.addInst(.{ |
| 2396 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32()); | 2153 | .tag = .movz, |
| 2397 | } else if (x <= math.maxInt(u32)) { | 2154 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, |
| 2398 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); | 2155 | }); |
| 2399 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32()); | 2156 | |
| 2400 | } else if (x <= math.maxInt(u32)) { | 2157 | if (x > math.maxInt(u16)) { |
| 2401 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); | 2158 | _ = try self.addInst(.{ |
| 2402 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); | 2159 | .tag = .movk, |
| 2403 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32()); | 2160 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, |
| 2404 | } else { | 2161 | }); |
| 2405 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); | 2162 | } |
| 2406 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); | 2163 | if (x > math.maxInt(u32)) { |
| 2407 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32()); | 2164 | _ = try self.addInst(.{ |
| 2408 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32()); | 2165 | .tag = .movk, |
| | 2166 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, |
| | 2167 | }); |
| | 2168 | } |
| | 2169 | if (x > math.maxInt(u48)) { |
| | 2170 | _ = try self.addInst(.{ |
| | 2171 | .tag = .movk, |
| | 2172 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, |
| | 2173 | }); |
| 2409 | } | 2174 | } |
| 2410 | }, | 2175 | }, |
| 2411 | .register => |src_reg| { | 2176 | .register => |src_reg| { |
| ... | @@ -2414,30 +2179,36 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2414,30 +2179,36 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2414 | return; | 2179 | return; |
| 2415 | | 2180 | |
| 2416 | // mov reg, src_reg | 2181 | // mov reg, src_reg |
| 2417 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr( | 2182 | _ = try self.addInst(.{ |
| 2418 | reg, | 2183 | .tag = .mov_register, |
| 2419 | .xzr, | 2184 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 2420 | src_reg, | 2185 | }); |
| 2421 | Instruction.Shift.none, | | |
| 2422 | ).toU32()); | | |
| 2423 | }, | 2186 | }, |
| 2424 | .memory => |addr| { | 2187 | .memory => |addr| { |
| 2425 | if (self.bin_file.options.pie) { | 2188 | if (self.bin_file.options.pie) { |
| 2426 | // PC-relative displacement to the entry in the GOT table. | 2189 | // PC-relative displacement to the entry in the GOT table. |
| 2427 | // adrp | 2190 | // adrp |
| 2428 | const offset = @intCast(u32, self.code.items.len); | 2191 | // TODO add a pseudo instruction |
| 2429 | mem.writeIntLittle( | 2192 | const offset = @intCast(u32, self.mir_instructions.len); |
| 2430 | u32, | 2193 | // mem.writeIntLittle( |
| 2431 | try self.code.addManyAsArray(4), | 2194 | // u32, |
| 2432 | Instruction.adrp(reg, 0).toU32(), | 2195 | // try self.code.addManyAsArray(4), |
| 2433 | ); | 2196 | // Instruction.adrp(reg, 0).toU32(), |
| | 2197 | // ); |
| | 2198 | _ = try self.addInst(.{ |
| | 2199 | .tag = .nop, |
| | 2200 | .data = .{ .nop = {} }, |
| | 2201 | }); |
| | 2202 | |
| 2434 | // ldr reg, reg, offset | 2203 | // ldr reg, reg, offset |
| 2435 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ | 2204 | _ = try self.addInst(.{ |
| 2436 | .register = .{ | 2205 | .tag = .ldr, |
| | 2206 | .data = .{ .load_store_register = .{ |
| | 2207 | .rt = reg, |
| 2437 | .rn = reg, | 2208 | .rn = reg, |
| 2438 | .offset = Instruction.LoadStoreOffset.imm(0), | 2209 | .offset = Instruction.LoadStoreOffset.imm(0), |
| 2439 | }, | 2210 | } }, |
| 2440 | }).toU32()); | 2211 | }); |
| 2441 | | 2212 | |
| 2442 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 2213 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 2443 | // TODO I think the reloc might be in the wrong place. | 2214 | // TODO I think the reloc might be in the wrong place. |
| ... | @@ -2469,7 +2240,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2469,7 +2240,14 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2469 | // The value is in memory at a hard-coded address. | 2240 | // The value is in memory at a hard-coded address. |
| 2470 | // If the type is a pointer, it means the pointer address is at this memory location. | 2241 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2471 | try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr }); | 2242 | try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr }); |
| 2472 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32()); | 2243 | _ = try self.addInst(.{ |
| | 2244 | .tag = .ldr, |
| | 2245 | .data = .{ .load_store_register = .{ |
| | 2246 | .rt = reg, |
| | 2247 | .rn = reg, |
| | 2248 | .offset = Instruction.LoadStoreOffset.none, |
| | 2249 | } }, |
| | 2250 | }); |
| 2473 | } | 2251 | } |
| 2474 | }, | 2252 | }, |
| 2475 | .stack_offset => |unadjusted_off| { | 2253 | .stack_offset => |unadjusted_off| { |
| ... | @@ -2489,22 +2267,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2489,22 +2267,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2489 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off })); | 2267 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off })); |
| 2490 | | 2268 | |
| 2491 | switch (abi_size) { | 2269 | switch (abi_size) { |
| 2492 | 1, 2 => { | 2270 | 1, 2, 4, 8 => { |
| 2493 | const ldr = switch (abi_size) { | 2271 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2494 | 1 => Instruction.ldrb, | 2272 | 1 => .ldrb, |
| 2495 | 2 => Instruction.ldrh, | 2273 | 2 => .ldrh, |
| | 2274 | 4, 8 => .ldr, |
| 2496 | else => unreachable, // unexpected abi size | 2275 | else => unreachable, // unexpected abi size |
| 2497 | }; | 2276 | }; |
| 2498 | | 2277 | |
| 2499 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{ | 2278 | _ = try self.addInst(.{ |
| 2500 | .offset = offset, | 2279 | .tag = tag, |
| 2501 | }).toU32()); | 2280 | .data = .{ .load_store_register = .{ |
| 2502 | }, | 2281 | .rt = reg, |
| 2503 | 4, 8 => { | 2282 | .rn = rn, |
| 2504 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ | 2283 | .offset = offset, |
| 2505 | .rn = rn, | 2284 | } }, |
| 2506 | .offset = offset, | 2285 | }); |
| 2507 | } }).toU32()); | | |
| 2508 | }, | 2286 | }, |
| 2509 | else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), | 2287 | else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), |
| 2510 | } | 2288 | } |