| ... | ... | @@ -5,6 +5,8 @@ const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | 7 | const Zir = @import("../../Zir.zig"); |
| 8 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); |
| 8 | 10 | const Liveness = @import("../../Liveness.zig"); |
| 9 | 11 | const Type = @import("../../type.zig").Type; |
| 10 | 12 | const Value = @import("../../value.zig").Value; |
| ... | ... | @@ -31,15 +33,12 @@ const InnerError = error{ |
| 31 | 33 | CodegenFail, |
| 32 | 34 | }; |
| 33 | 35 | |
| 34 | | arch: std.Target.Cpu.Arch, |
| 35 | 36 | gpa: *Allocator, |
| 36 | 37 | air: Air, |
| 37 | 38 | liveness: Liveness, |
| 38 | 39 | bin_file: *link.File, |
| 39 | 40 | target: *const std.Target, |
| 40 | 41 | mod_fn: *const Module.Fn, |
| 41 | | code: *std.ArrayList(u8), |
| 42 | | debug_output: DebugInfoOutput, |
| 43 | 42 | err_msg: ?*ErrorMsg, |
| 44 | 43 | args: []MCValue, |
| 45 | 44 | ret_mcv: MCValue, |
| ... | ... | @@ -48,13 +47,14 @@ arg_index: usize, |
| 48 | 47 | src_loc: Module.SrcLoc, |
| 49 | 48 | stack_align: u32, |
| 50 | 49 | |
| 51 | | prev_di_line: u32, |
| 52 | | prev_di_column: u32, |
| 50 | /// MIR Instructions |
| 51 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 52 | /// MIR extra data |
| 53 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 54 | |
| 53 | 55 | /// Byte offset within the source file of the ending curly. |
| 54 | 56 | end_di_line: u32, |
| 55 | 57 | end_di_column: u32, |
| 56 | | /// Relative to the beginning of `code`. |
| 57 | | prev_di_pc: usize, |
| 58 | 58 | |
| 59 | 59 | /// The value is an offset into the `Function` `code` from the beginning. |
| 60 | 60 | /// To perform the reloc, write 32-bit signed little-endian integer |
| ... | ... | @@ -237,7 +237,6 @@ const BigTomb = struct { |
| 237 | 237 | const Self = @This(); |
| 238 | 238 | |
| 239 | 239 | pub fn generate( |
| 240 | | arch: std.Target.Cpu.Arch, |
| 241 | 240 | bin_file: *link.File, |
| 242 | 241 | src_loc: Module.SrcLoc, |
| 243 | 242 | module_fn: *Module.Fn, |
| ... | ... | @@ -246,7 +245,7 @@ pub fn generate( |
| 246 | 245 | code: *std.ArrayList(u8), |
| 247 | 246 | debug_output: DebugInfoOutput, |
| 248 | 247 | ) GenerateSymbolError!FnResult { |
| 249 | | if (build_options.skip_non_native and builtin.cpu.arch != arch) { |
| 248 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 250 | 249 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 251 | 250 | } |
| 252 | 251 | |
| ... | ... | @@ -262,15 +261,12 @@ pub fn generate( |
| 262 | 261 | try branch_stack.append(.{}); |
| 263 | 262 | |
| 264 | 263 | var function = Self{ |
| 265 | | .arch = arch, |
| 266 | 264 | .gpa = bin_file.allocator, |
| 267 | 265 | .air = air, |
| 268 | 266 | .liveness = liveness, |
| 269 | 267 | .target = &bin_file.options.target, |
| 270 | 268 | .bin_file = bin_file, |
| 271 | 269 | .mod_fn = module_fn, |
| 272 | | .code = code, |
| 273 | | .debug_output = debug_output, |
| 274 | 270 | .err_msg = null, |
| 275 | 271 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 276 | 272 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| ... | ... | @@ -279,9 +275,6 @@ pub fn generate( |
| 279 | 275 | .branch_stack = &branch_stack, |
| 280 | 276 | .src_loc = src_loc, |
| 281 | 277 | .stack_align = undefined, |
| 282 | | .prev_di_pc = 0, |
| 283 | | .prev_di_line = module_fn.lbrace_line, |
| 284 | | .prev_di_column = module_fn.lbrace_column, |
| 285 | 278 | .end_di_line = module_fn.rbrace_line, |
| 286 | 279 | .end_di_column = module_fn.rbrace_column, |
| 287 | 280 | }; |
| ... | ... | @@ -305,6 +298,28 @@ pub fn generate( |
| 305 | 298 | else => |e| return e, |
| 306 | 299 | }; |
| 307 | 300 | |
| 301 | var mir = Mir{ |
| 302 | .instructions = function.mir_instructions.toOwnedSlice(), |
| 303 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), |
| 304 | }; |
| 305 | defer mir.deinit(bin_file.allocator); |
| 306 | |
| 307 | var emit = Emit{ |
| 308 | .mir = mir, |
| 309 | .bin_file = bin_file, |
| 310 | .debug_output = debug_output, |
| 311 | .target = &bin_file.options.target, |
| 312 | .src_loc = src_loc, |
| 313 | .code = code, |
| 314 | .prev_di_pc = 0, |
| 315 | .prev_di_line = module_fn.lbrace_line, |
| 316 | .prev_di_column = module_fn.lbrace_column, |
| 317 | }; |
| 318 | emit.emitMir() catch |err| switch (err) { |
| 319 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, |
| 320 | else => |e| return e, |
| 321 | }; |
| 322 | |
| 308 | 323 | if (function.err_msg) |em| { |
| 309 | 324 | return FnResult{ .fail = em }; |
| 310 | 325 | } else { |
| ... | ... | @@ -312,6 +327,35 @@ 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 | |
| 340 | pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { |
| 341 | const fields = std.meta.fields(@TypeOf(extra)); |
| 342 | try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); |
| 343 | return self.addExtraAssumeCapacity(extra); |
| 344 | } |
| 345 | |
| 346 | pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 347 | const fields = std.meta.fields(@TypeOf(extra)); |
| 348 | const result = @intCast(u32, self.mir_extra.items.len); |
| 349 | inline for (fields) |field| { |
| 350 | self.mir_extra.appendAssumeCapacity(switch (field.field_type) { |
| 351 | u32 => @field(extra, field.name), |
| 352 | i32 => @bitCast(u32, @field(extra, field.name)), |
| 353 | else => @compileError("bad field type"), |
| 354 | }); |
| 355 | } |
| 356 | return result; |
| 357 | } |
| 358 | |
| 315 | 359 | fn gen(self: *Self) !void { |
| 316 | 360 | const cc = self.fn_type.fnCallingConvention(); |
| 317 | 361 | if (cc != .Naked) { |
| ... | ... | @@ -320,17 +364,31 @@ fn gen(self: *Self) !void { |
| 320 | 364 | // stp fp, lr, [sp, #-16]! |
| 321 | 365 | // mov fp, sp |
| 322 | 366 | // sub sp, sp, #reloc |
| 323 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp( |
| 324 | | .x29, |
| 325 | | .x30, |
| 326 | | Register.sp, |
| 327 | | Instruction.LoadStorePairOffset.pre_index(-16), |
| 328 | | ).toU32()); |
| 329 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32()); |
| 330 | | const backpatch_reloc = self.code.items.len; |
| 331 | | try self.code.resize(backpatch_reloc + 4); |
| 332 | | |
| 333 | | try self.dbgSetPrologueEnd(); |
| 367 | |
| 368 | _ = try self.addInst(.{ |
| 369 | .tag = .stp, |
| 370 | .data = .{ .load_store_register_pair = .{ |
| 371 | .rt = .x29, |
| 372 | .rt2 = .x30, |
| 373 | .rn = Register.sp, |
| 374 | .offset = Instruction.LoadStorePairOffset.pre_index(-16), |
| 375 | } }, |
| 376 | }); |
| 377 | |
| 378 | _ = try self.addInst(.{ |
| 379 | .tag = .mov_to_from_sp, |
| 380 | .data = .{ .rr = .{ .rd = .x29, .rn = .xzr } }, |
| 381 | }); |
| 382 | |
| 383 | const backpatch_reloc = try self.addInst(.{ |
| 384 | .tag = .nop, |
| 385 | .data = .{ .nop = {} }, |
| 386 | }); |
| 387 | |
| 388 | _ = try self.addInst(.{ |
| 389 | .tag = .dbg_prologue_end, |
| 390 | .data = .{ .nop = {} }, |
| 391 | }); |
| 334 | 392 | |
| 335 | 393 | try self.genBody(self.air.getMainBody()); |
| 336 | 394 | |
| ... | ... | @@ -338,12 +396,18 @@ fn gen(self: *Self) !void { |
| 338 | 396 | const stack_end = self.max_end_stack; |
| 339 | 397 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 340 | 398 | 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()); |
| 399 | self.mir_instructions.set(backpatch_reloc, .{ |
| 400 | .tag = .sub_immediate, |
| 401 | .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = size } }, |
| 402 | }); |
| 342 | 403 | } else |_| { |
| 343 | 404 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); |
| 344 | 405 | } |
| 345 | 406 | |
| 346 | | try self.dbgSetEpilogueBegin(); |
| 407 | _ = try self.addInst(.{ |
| 408 | .tag = .dbg_epilogue_begin, |
| 409 | .data = .{ .nop = {} }, |
| 410 | }); |
| 347 | 411 | |
| 348 | 412 | // exitlude jumps |
| 349 | 413 | if (self.exitlude_jump_relocs.items.len == 1) { |
| ... | ... | @@ -352,44 +416,58 @@ fn gen(self: *Self) !void { |
| 352 | 416 | // the code. Therefore, we can just delete |
| 353 | 417 | // the space initially reserved for the |
| 354 | 418 | // jump |
| 355 | | self.code.items.len -= 4; |
| 419 | self.mir_instructions.len -= 1; |
| 356 | 420 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 357 | | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8); |
| 358 | | if (amt == -4) { |
| 359 | | // This return is at the end of the |
| 360 | | // code block. We can't just delete |
| 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 | | } |
| 421 | self.mir_instructions.set(jmp_reloc, .{ |
| 422 | .tag = .b, |
| 423 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, |
| 424 | }); |
| 372 | 425 | } |
| 373 | 426 | |
| 374 | 427 | // ldp fp, lr, [sp], #16 |
| 375 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp( |
| 376 | | .x29, |
| 377 | | .x30, |
| 378 | | Register.sp, |
| 379 | | Instruction.LoadStorePairOffset.post_index(16), |
| 380 | | ).toU32()); |
| 428 | _ = try self.addInst(.{ |
| 429 | .tag = .ldp, |
| 430 | .data = .{ .load_store_register_pair = .{ |
| 431 | .rt = .x29, |
| 432 | .rt2 = .x30, |
| 433 | .rn = Register.sp, |
| 434 | .offset = Instruction.LoadStorePairOffset.post_index(16), |
| 435 | } }, |
| 436 | }); |
| 437 | |
| 381 | 438 | // 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()); |
| 439 | _ = try self.addInst(.{ |
| 440 | .tag = .add_immediate, |
| 441 | .data = .{ .rr_imm12_sh = .{ .rd = .xzr, .rn = .xzr, .imm12 = @intCast(u12, aligned_stack_end) } }, |
| 442 | }); |
| 443 | |
| 383 | 444 | // ret lr |
| 384 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32()); |
| 445 | _ = try self.addInst(.{ |
| 446 | .tag = .ret, |
| 447 | .data = .{ .reg = .x30 }, |
| 448 | }); |
| 385 | 449 | } else { |
| 386 | | try self.dbgSetPrologueEnd(); |
| 450 | _ = try self.addInst(.{ |
| 451 | .tag = .dbg_prologue_end, |
| 452 | .data = .{ .nop = {} }, |
| 453 | }); |
| 454 | |
| 387 | 455 | try self.genBody(self.air.getMainBody()); |
| 388 | | try self.dbgSetEpilogueBegin(); |
| 456 | |
| 457 | _ = try self.addInst(.{ |
| 458 | .tag = .dbg_epilogue_begin, |
| 459 | .data = .{ .nop = {} }, |
| 460 | }); |
| 389 | 461 | } |
| 390 | 462 | |
| 391 | 463 | // Drop them off at the rbrace. |
| 392 | | try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column); |
| 464 | _ = try self.addInst(.{ |
| 465 | .tag = .dbg_line, |
| 466 | .data = .{ .dbg_line_column = .{ |
| 467 | .line = self.end_di_line, |
| 468 | .column = self.end_di_column, |
| 469 | } }, |
| 470 | }); |
| 393 | 471 | } |
| 394 | 472 | |
| 395 | 473 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -530,79 +608,6 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 530 | 608 | } |
| 531 | 609 | } |
| 532 | 610 | |
| 533 | | fn dbgSetPrologueEnd(self: *Self) InnerError!void { |
| 534 | | switch (self.debug_output) { |
| 535 | | .dwarf => |dbg_out| { |
| 536 | | try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); |
| 537 | | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 538 | | }, |
| 539 | | .plan9 => {}, |
| 540 | | .none => {}, |
| 541 | | } |
| 542 | | } |
| 543 | | |
| 544 | | fn dbgSetEpilogueBegin(self: *Self) InnerError!void { |
| 545 | | switch (self.debug_output) { |
| 546 | | .dwarf => |dbg_out| { |
| 547 | | try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); |
| 548 | | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); |
| 549 | | }, |
| 550 | | .plan9 => {}, |
| 551 | | .none => {}, |
| 552 | | } |
| 553 | | } |
| 554 | | |
| 555 | | fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void { |
| 556 | | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); |
| 557 | | const delta_pc: usize = self.code.items.len - self.prev_di_pc; |
| 558 | | switch (self.debug_output) { |
| 559 | | .dwarf => |dbg_out| { |
| 560 | | // TODO Look into using the DWARF special opcodes to compress this data. |
| 561 | | // It lets you emit single-byte opcodes that add different numbers to |
| 562 | | // both the PC and the line number at the same time. |
| 563 | | try dbg_out.dbg_line.ensureUnusedCapacity(11); |
| 564 | | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); |
| 565 | | leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; |
| 566 | | if (delta_line != 0) { |
| 567 | | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); |
| 568 | | leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; |
| 569 | | } |
| 570 | | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); |
| 571 | | self.prev_di_pc = self.code.items.len; |
| 572 | | self.prev_di_line = line; |
| 573 | | self.prev_di_column = column; |
| 574 | | self.prev_di_pc = self.code.items.len; |
| 575 | | }, |
| 576 | | .plan9 => |dbg_out| { |
| 577 | | if (delta_pc <= 0) return; // only do this when the pc changes |
| 578 | | // we have already checked the target in the linker to make sure it is compatable |
| 579 | | const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; |
| 580 | | |
| 581 | | // increasing the line number |
| 582 | | try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); |
| 583 | | // increasing the pc |
| 584 | | const d_pc_p9 = @intCast(i64, delta_pc) - quant; |
| 585 | | if (d_pc_p9 > 0) { |
| 586 | | // minus one because if its the last one, we want to leave space to change the line which is one quanta |
| 587 | | try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); |
| 588 | | if (dbg_out.pcop_change_index.*) |pci| |
| 589 | | dbg_out.dbg_line.items[pci] += 1; |
| 590 | | dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); |
| 591 | | } else if (d_pc_p9 == 0) { |
| 592 | | // we don't need to do anything, because adding the quant does it for us |
| 593 | | } else unreachable; |
| 594 | | if (dbg_out.start_line.* == null) |
| 595 | | dbg_out.start_line.* = self.prev_di_line; |
| 596 | | dbg_out.end_line.* = line; |
| 597 | | // only do this if the pc changed |
| 598 | | self.prev_di_line = line; |
| 599 | | self.prev_di_column = column; |
| 600 | | self.prev_di_pc = self.code.items.len; |
| 601 | | }, |
| 602 | | .none => {}, |
| 603 | | } |
| 604 | | } |
| 605 | | |
| 606 | 611 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 607 | 612 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 608 | 613 | const air_tags = self.air.instructions.items(.tag); |
| ... | ... | @@ -1297,310 +1302,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1297 | 1302 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 1298 | 1303 | } |
| 1299 | 1304 | |
| 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 | 1305 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1605 | 1306 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; |
| 1606 | 1307 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; |
| ... | ... | @@ -1652,7 +1353,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1652 | 1353 | }, |
| 1653 | 1354 | else => result, |
| 1654 | 1355 | }; |
| 1655 | | try self.genArgDbgInfo(inst, mcv); |
| 1356 | // TODO generate debug info |
| 1357 | // try self.genArgDbgInfo(inst, mcv); |
| 1656 | 1358 | |
| 1657 | 1359 | if (self.liveness.isUnused(inst)) |
| 1658 | 1360 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -1668,7 +1370,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1668 | 1370 | } |
| 1669 | 1371 | |
| 1670 | 1372 | fn airBreakpoint(self: *Self) !void { |
| 1671 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32()); |
| 1373 | _ = try self.addInst(.{ |
| 1374 | .tag = .brk, |
| 1375 | .data = .{ .imm16 = 1 }, |
| 1376 | }); |
| 1672 | 1377 | return self.finishAirBookkeeping(); |
| 1673 | 1378 | } |
| 1674 | 1379 | |
| ... | ... | @@ -1736,7 +1441,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1736 | 1441 | |
| 1737 | 1442 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 1738 | 1443 | |
| 1739 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 1444 | _ = try self.addInst(.{ |
| 1445 | .tag = .blr, |
| 1446 | .data = .{ .reg = .x30 }, |
| 1447 | }); |
| 1740 | 1448 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1741 | 1449 | return self.fail("TODO implement calling extern functions", .{}); |
| 1742 | 1450 | } else { |
| ... | ... | @@ -1789,25 +1497,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1789 | 1497 | .memory = func.owner_decl.link.macho.local_sym_index, |
| 1790 | 1498 | }); |
| 1791 | 1499 | // blr x30 |
| 1792 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 1500 | _ = try self.addInst(.{ |
| 1501 | .tag = .blr, |
| 1502 | .data = .{ .reg = .x30 }, |
| 1503 | }); |
| 1793 | 1504 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 1794 | 1505 | const decl = func_payload.data; |
| 1795 | 1506 | const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name)); |
| 1796 | | const offset = blk: { |
| 1797 | | const offset = @intCast(u32, self.code.items.len); |
| 1798 | | // bl |
| 1799 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); |
| 1800 | | break :blk offset; |
| 1801 | | }; |
| 1802 | | // Add relocation to the decl. |
| 1803 | | try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{ |
| 1804 | | .offset = offset, |
| 1805 | | .target = .{ .global = n_strx }, |
| 1806 | | .addend = 0, |
| 1807 | | .subtractor = null, |
| 1808 | | .pcrel = true, |
| 1809 | | .length = 2, |
| 1810 | | .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), |
| 1507 | |
| 1508 | _ = try self.addInst(.{ |
| 1509 | .tag = .call_extern, |
| 1510 | .data = .{ .extern_fn = n_strx }, |
| 1811 | 1511 | }); |
| 1812 | 1512 | } else { |
| 1813 | 1513 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| ... | ... | @@ -1857,7 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1857 | 1557 | |
| 1858 | 1558 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); |
| 1859 | 1559 | |
| 1860 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 1560 | _ = try self.addInst(.{ |
| 1561 | .tag = .blr, |
| 1562 | .data = .{ .reg = .x30 }, |
| 1563 | }); |
| 1861 | 1564 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1862 | 1565 | return self.fail("TODO implement calling extern functions", .{}); |
| 1863 | 1566 | } else { |
| ... | ... | @@ -1899,8 +1602,11 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 1899 | 1602 | const ret_ty = self.fn_type.fnReturnType(); |
| 1900 | 1603 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1901 | 1604 | // Just add space for an instruction, patch this later |
| 1902 | | try self.code.resize(self.code.items.len + 4); |
| 1903 | | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); |
| 1605 | const index = try self.addInst(.{ |
| 1606 | .tag = .nop, |
| 1607 | .data = .{ .nop = {} }, |
| 1608 | }); |
| 1609 | try self.exitlude_jump_relocs.append(self.gpa, index); |
| 1904 | 1610 | } |
| 1905 | 1611 | |
| 1906 | 1612 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1939,7 +1645,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1939 | 1645 | |
| 1940 | 1646 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1941 | 1647 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 1942 | | try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column); |
| 1648 | |
| 1649 | _ = try self.addInst(.{ |
| 1650 | .tag = .dbg_line, |
| 1651 | .data = .{ .dbg_line_column = .{ |
| 1652 | .line = dbg_stmt.line, |
| 1653 | .column = dbg_stmt.column, |
| 1654 | } }, |
| 1655 | }); |
| 1656 | |
| 1943 | 1657 | return self.finishAirBookkeeping(); |
| 1944 | 1658 | } |
| 1945 | 1659 | |
| ... | ... | @@ -2090,19 +1804,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 2090 | 1804 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2091 | 1805 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2092 | 1806 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 2093 | | const start_index = self.code.items.len; |
| 1807 | const start_index = @intCast(u32, self.mir_instructions.len); |
| 2094 | 1808 | try self.genBody(body); |
| 2095 | 1809 | try self.jump(start_index); |
| 2096 | 1810 | return self.finishAirBookkeeping(); |
| 2097 | 1811 | } |
| 2098 | 1812 | |
| 2099 | | /// Send control flow to the `index` of `self.code`. |
| 2100 | | fn jump(self: *Self, index: usize) !void { |
| 2101 | | if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { |
| 2102 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32()); |
| 2103 | | } else |_| { |
| 2104 | | return self.fail("TODO: enable larger branch offset", .{}); |
| 2105 | | } |
| 1813 | /// Send control flow to `inst`. |
| 1814 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { |
| 1815 | _ = try self.addInst(.{ |
| 1816 | .tag = .b, |
| 1817 | .data = .{ .inst = inst }, |
| 1818 | }); |
| 2106 | 1819 | } |
| 2107 | 1820 | |
| 2108 | 1821 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2140,19 +1853,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2140 | 1853 | |
| 2141 | 1854 | fn performReloc(self: *Self, reloc: Reloc) !void { |
| 2142 | 1855 | switch (reloc) { |
| 2143 | | .rel32 => |pos| { |
| 2144 | | const amt = self.code.items.len - (pos + 4); |
| 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, |
| 1856 | .rel32 => return self.fail("TODO reloc.rel32 for {}", .{self.target.cpu.arch}), |
| 1857 | .arm_branch => return self.fail("TODO reloc.arm_branch for {}", .{self.target.cpu.arch}), |
| 2156 | 1858 | } |
| 2157 | 1859 | } |
| 2158 | 1860 | |
| ... | ... | @@ -2244,9 +1946,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2244 | 1946 | } |
| 2245 | 1947 | |
| 2246 | 1948 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2247 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32()); |
| 1949 | _ = try self.addInst(.{ |
| 1950 | .tag = .svc, |
| 1951 | .data = .{ .imm16 = 0x0 }, |
| 1952 | }); |
| 2248 | 1953 | } else if (mem.eql(u8, asm_source, "svc #0x80")) { |
| 2249 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32()); |
| 1954 | _ = try self.addInst(.{ |
| 1955 | .tag = .svc, |
| 1956 | .data = .{ .imm16 = 0x80 }, |
| 1957 | }); |
| 2250 | 1958 | } else { |
| 2251 | 1959 | return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); |
| 2252 | 1960 | } |
| ... | ... | @@ -2333,6 +2041,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2333 | 2041 | return self.fail("TODO implement set stack variable from embedded_in_code", .{}); |
| 2334 | 2042 | }, |
| 2335 | 2043 | .register => |reg| { |
| 2044 | _ = reg; |
| 2045 | |
| 2336 | 2046 | const abi_size = ty.abiSize(self.target.*); |
| 2337 | 2047 | const adj_off = stack_offset + abi_size; |
| 2338 | 2048 | |
| ... | ... | @@ -2347,16 +2057,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2347 | 2057 | .aarch64_32 => .w29, |
| 2348 | 2058 | else => unreachable, |
| 2349 | 2059 | }; |
| 2350 | | const str = switch (abi_size) { |
| 2351 | | 1 => Instruction.strb, |
| 2352 | | 2 => Instruction.strh, |
| 2353 | | 4, 8 => Instruction.str, |
| 2060 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2061 | 1 => .strb, |
| 2062 | 2 => .strh, |
| 2063 | 4, 8 => .str, |
| 2354 | 2064 | else => unreachable, // unexpected abi size |
| 2355 | 2065 | }; |
| 2356 | 2066 | |
| 2357 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{ |
| 2358 | | .offset = offset, |
| 2359 | | }).toU32()); |
| 2067 | _ = try self.addInst(.{ |
| 2068 | .tag = tag, |
| 2069 | .data = .{ .load_store_register = .{ |
| 2070 | .rt = reg, |
| 2071 | .rn = rn, |
| 2072 | .offset = offset, |
| 2073 | } }, |
| 2074 | }); |
| 2360 | 2075 | }, |
| 2361 | 2076 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 2362 | 2077 | } |
| ... | ... | @@ -2392,20 +2107,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2392 | 2107 | } |
| 2393 | 2108 | }, |
| 2394 | 2109 | .immediate => |x| { |
| 2395 | | if (x <= math.maxInt(u16)) { |
| 2396 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32()); |
| 2397 | | } else if (x <= math.maxInt(u32)) { |
| 2398 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); |
| 2399 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32()); |
| 2400 | | } else if (x <= math.maxInt(u32)) { |
| 2401 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); |
| 2402 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); |
| 2403 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32()); |
| 2404 | | } else { |
| 2405 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); |
| 2406 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); |
| 2407 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32()); |
| 2408 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32()); |
| 2110 | _ = try self.addInst(.{ |
| 2111 | .tag = .movz, |
| 2112 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, |
| 2113 | }); |
| 2114 | |
| 2115 | if (x > math.maxInt(u16)) { |
| 2116 | _ = try self.addInst(.{ |
| 2117 | .tag = .movk, |
| 2118 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, |
| 2119 | }); |
| 2120 | } |
| 2121 | if (x > math.maxInt(u32)) { |
| 2122 | _ = try self.addInst(.{ |
| 2123 | .tag = .movk, |
| 2124 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, |
| 2125 | }); |
| 2126 | } |
| 2127 | if (x > math.maxInt(u48)) { |
| 2128 | _ = try self.addInst(.{ |
| 2129 | .tag = .movk, |
| 2130 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, |
| 2131 | }); |
| 2409 | 2132 | } |
| 2410 | 2133 | }, |
| 2411 | 2134 | .register => |src_reg| { |
| ... | ... | @@ -2414,63 +2137,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2414 | 2137 | return; |
| 2415 | 2138 | |
| 2416 | 2139 | // mov reg, src_reg |
| 2417 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr( |
| 2418 | | reg, |
| 2419 | | .xzr, |
| 2420 | | src_reg, |
| 2421 | | Instruction.Shift.none, |
| 2422 | | ).toU32()); |
| 2140 | _ = try self.addInst(.{ |
| 2141 | .tag = .mov_register, |
| 2142 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 2143 | }); |
| 2423 | 2144 | }, |
| 2424 | 2145 | .memory => |addr| { |
| 2425 | | if (self.bin_file.options.pie) { |
| 2426 | | // PC-relative displacement to the entry in the GOT table. |
| 2427 | | // adrp |
| 2428 | | const offset = @intCast(u32, self.code.items.len); |
| 2429 | | mem.writeIntLittle( |
| 2430 | | u32, |
| 2431 | | try self.code.addManyAsArray(4), |
| 2432 | | Instruction.adrp(reg, 0).toU32(), |
| 2433 | | ); |
| 2434 | | // ldr reg, reg, offset |
| 2435 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ |
| 2436 | | .register = .{ |
| 2437 | | .rn = reg, |
| 2438 | | .offset = Instruction.LoadStoreOffset.imm(0), |
| 2439 | | }, |
| 2440 | | }).toU32()); |
| 2441 | | |
| 2442 | | if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 2443 | | // TODO I think the reloc might be in the wrong place. |
| 2444 | | const decl = macho_file.active_decl.?; |
| 2445 | | // Page reloc for adrp instruction. |
| 2446 | | try decl.link.macho.relocs.append(self.bin_file.allocator, .{ |
| 2447 | | .offset = offset, |
| 2448 | | .target = .{ .local = @intCast(u32, addr) }, |
| 2449 | | .addend = 0, |
| 2450 | | .subtractor = null, |
| 2451 | | .pcrel = true, |
| 2452 | | .length = 2, |
| 2453 | | .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21), |
| 2454 | | }); |
| 2455 | | // Pageoff reloc for adrp instruction. |
| 2456 | | try decl.link.macho.relocs.append(self.bin_file.allocator, .{ |
| 2457 | | .offset = offset + 4, |
| 2458 | | .target = .{ .local = @intCast(u32, addr) }, |
| 2459 | | .addend = 0, |
| 2460 | | .subtractor = null, |
| 2461 | | .pcrel = false, |
| 2462 | | .length = 2, |
| 2463 | | .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12), |
| 2464 | | }); |
| 2465 | | } else { |
| 2466 | | return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{}); |
| 2467 | | } |
| 2468 | | } else { |
| 2469 | | // 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. |
| 2471 | | 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()); |
| 2473 | | } |
| 2146 | _ = try self.addInst(.{ |
| 2147 | .tag = .load_memory, |
| 2148 | .data = .{ .payload = try self.addExtra(Mir.LoadMemory{ |
| 2149 | .register = @enumToInt(reg), |
| 2150 | .addr = @intCast(u32, addr), |
| 2151 | }) }, |
| 2152 | }); |
| 2474 | 2153 | }, |
| 2475 | 2154 | .stack_offset => |unadjusted_off| { |
| 2476 | 2155 | // TODO: maybe addressing from sp instead of fp |
| ... | ... | @@ -2489,22 +2168,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2489 | 2168 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off })); |
| 2490 | 2169 | |
| 2491 | 2170 | switch (abi_size) { |
| 2492 | | 1, 2 => { |
| 2493 | | const ldr = switch (abi_size) { |
| 2494 | | 1 => Instruction.ldrb, |
| 2495 | | 2 => Instruction.ldrh, |
| 2171 | 1, 2, 4, 8 => { |
| 2172 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2173 | 1 => .ldrb, |
| 2174 | 2 => .ldrh, |
| 2175 | 4, 8 => .ldr, |
| 2496 | 2176 | else => unreachable, // unexpected abi size |
| 2497 | 2177 | }; |
| 2498 | 2178 | |
| 2499 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{ |
| 2500 | | .offset = offset, |
| 2501 | | }).toU32()); |
| 2502 | | }, |
| 2503 | | 4, 8 => { |
| 2504 | | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ |
| 2505 | | .rn = rn, |
| 2506 | | .offset = offset, |
| 2507 | | } }).toU32()); |
| 2179 | _ = try self.addInst(.{ |
| 2180 | .tag = tag, |
| 2181 | .data = .{ .load_store_register = .{ |
| 2182 | .rt = reg, |
| 2183 | .rn = rn, |
| 2184 | .offset = offset, |
| 2185 | } }, |
| 2186 | }); |
| 2508 | 2187 | }, |
| 2509 | 2188 | else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), |
| 2510 | 2189 | } |