| ... | @@ -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,15 +33,12 @@ const InnerError = error{ | ... | @@ -31,15 +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, | | |
| 43 | err_msg: ?*ErrorMsg, | 42 | err_msg: ?*ErrorMsg, |
| 44 | args: []MCValue, | 43 | args: []MCValue, |
| 45 | ret_mcv: MCValue, | 44 | ret_mcv: MCValue, |
| ... | @@ -48,13 +47,14 @@ arg_index: usize, | ... | @@ -48,13 +47,14 @@ arg_index: usize, |
| 48 | src_loc: Module.SrcLoc, | 47 | src_loc: Module.SrcLoc, |
| 49 | stack_align: u32, | 48 | stack_align: u32, |
| 50 | | 49 | |
| 51 | prev_di_line: u32, | 50 | /// MIR Instructions |
| 52 | prev_di_column: u32, | 51 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| | 52 | /// MIR extra data |
| | 53 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| | 54 | |
| 53 | /// Byte offset within the source file of the ending curly. | 55 | /// Byte offset within the source file of the ending curly. |
| 54 | end_di_line: u32, | 56 | end_di_line: u32, |
| 55 | end_di_column: u32, | 57 | end_di_column: u32, |
| 56 | /// Relative to the beginning of `code`. | | |
| 57 | prev_di_pc: usize, | | |
| 58 | | 58 | |
| 59 | /// The value is an offset into the `Function` `code` from the beginning. | 59 | /// The value is an offset into the `Function` `code` from the beginning. |
| 60 | /// To perform the reloc, write 32-bit signed little-endian integer | 60 | /// To perform the reloc, write 32-bit signed little-endian integer |
| ... | @@ -237,7 +237,6 @@ const BigTomb = struct { | ... | @@ -237,7 +237,6 @@ const BigTomb = struct { |
| 237 | const Self = @This(); | 237 | const Self = @This(); |
| 238 | | 238 | |
| 239 | pub fn generate( | 239 | pub fn generate( |
| 240 | arch: std.Target.Cpu.Arch, | | |
| 241 | bin_file: *link.File, | 240 | bin_file: *link.File, |
| 242 | src_loc: Module.SrcLoc, | 241 | src_loc: Module.SrcLoc, |
| 243 | module_fn: *Module.Fn, | 242 | module_fn: *Module.Fn, |
| ... | @@ -246,7 +245,7 @@ pub fn generate( | ... | @@ -246,7 +245,7 @@ pub fn generate( |
| 246 | code: *std.ArrayList(u8), | 245 | code: *std.ArrayList(u8), |
| 247 | debug_output: DebugInfoOutput, | 246 | debug_output: DebugInfoOutput, |
| 248 | ) GenerateSymbolError!FnResult { | 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 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 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,15 +261,12 @@ pub fn generate( |
| 262 | try branch_stack.append(.{}); | 261 | try branch_stack.append(.{}); |
| 263 | | 262 | |
| 264 | var function = Self{ | 263 | var function = Self{ |
| 265 | .arch = arch, | | |
| 266 | .gpa = bin_file.allocator, | 264 | .gpa = bin_file.allocator, |
| 267 | .air = air, | 265 | .air = air, |
| 268 | .liveness = liveness, | 266 | .liveness = liveness, |
| 269 | .target = &bin_file.options.target, | 267 | .target = &bin_file.options.target, |
| 270 | .bin_file = bin_file, | 268 | .bin_file = bin_file, |
| 271 | .mod_fn = module_fn, | 269 | .mod_fn = module_fn, |
| 272 | .code = code, | | |
| 273 | .debug_output = debug_output, | | |
| 274 | .err_msg = null, | 270 | .err_msg = null, |
| 275 | .args = undefined, // populated after `resolveCallingConventionValues` | 271 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 276 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 272 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| ... | @@ -279,9 +275,6 @@ pub fn generate( | ... | @@ -279,9 +275,6 @@ pub fn generate( |
| 279 | .branch_stack = &branch_stack, | 275 | .branch_stack = &branch_stack, |
| 280 | .src_loc = src_loc, | 276 | .src_loc = src_loc, |
| 281 | .stack_align = undefined, | 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 | .end_di_line = module_fn.rbrace_line, | 278 | .end_di_line = module_fn.rbrace_line, |
| 286 | .end_di_column = module_fn.rbrace_column, | 279 | .end_di_column = module_fn.rbrace_column, |
| 287 | }; | 280 | }; |
| ... | @@ -305,6 +298,28 @@ pub fn generate( | ... | @@ -305,6 +298,28 @@ pub fn generate( |
| 305 | else => |e| return e, | 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 | 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,35 @@ pub fn generate( | ... | @@ -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 | fn gen(self: *Self) !void { | 359 | fn gen(self: *Self) !void { |
| 316 | const cc = self.fn_type.fnCallingConvention(); | 360 | const cc = self.fn_type.fnCallingConvention(); |
| 317 | if (cc != .Naked) { | 361 | if (cc != .Naked) { |
| ... | @@ -320,17 +364,31 @@ fn gen(self: *Self) !void { | ... | @@ -320,17 +364,31 @@ fn gen(self: *Self) !void { |
| 320 | // stp fp, lr, [sp, #-16]! | 364 | // stp fp, lr, [sp, #-16]! |
| 321 | // mov fp, sp | 365 | // mov fp, sp |
| 322 | // sub sp, sp, #reloc | 366 | // sub sp, sp, #reloc |
| 323 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp( | 367 | |
| 324 | .x29, | 368 | _ = try self.addInst(.{ |
| 325 | .x30, | 369 | .tag = .stp, |
| 326 | Register.sp, | 370 | .data = .{ .load_store_register_pair = .{ |
| 327 | Instruction.LoadStorePairOffset.pre_index(-16), | 371 | .rt = .x29, |
| 328 | ).toU32()); | 372 | .rt2 = .x30, |
| 329 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32()); | 373 | .rn = Register.sp, |
| 330 | const backpatch_reloc = self.code.items.len; | 374 | .offset = Instruction.LoadStorePairOffset.pre_index(-16), |
| 331 | try self.code.resize(backpatch_reloc + 4); | 375 | } }, |
| 332 | | 376 | }); |
| 333 | try self.dbgSetPrologueEnd(); | 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 | try self.genBody(self.air.getMainBody()); | 393 | try self.genBody(self.air.getMainBody()); |
| 336 | | 394 | |
| ... | @@ -338,12 +396,18 @@ fn gen(self: *Self) !void { | ... | @@ -338,12 +396,18 @@ fn gen(self: *Self) !void { |
| 338 | const stack_end = self.max_end_stack; | 396 | const stack_end = self.max_end_stack; |
| 339 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); | 397 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 340 | if (math.cast(u12, aligned_stack_end)) |size| { | 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 | } else |_| { | 403 | } else |_| { |
| 343 | return self.failSymbol("TODO AArch64: allow larger stacks", .{}); | 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 | // exitlude jumps | 412 | // exitlude jumps |
| 349 | if (self.exitlude_jump_relocs.items.len == 1) { | 413 | if (self.exitlude_jump_relocs.items.len == 1) { |
| ... | @@ -352,44 +416,58 @@ fn gen(self: *Self) !void { | ... | @@ -352,44 +416,58 @@ fn gen(self: *Self) !void { |
| 352 | // the code. Therefore, we can just delete | 416 | // the code. Therefore, we can just delete |
| 353 | // the space initially reserved for the | 417 | // the space initially reserved for the |
| 354 | // jump | 418 | // jump |
| 355 | self.code.items.len -= 4; | 419 | self.mir_instructions.len -= 1; |
| 356 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { | 420 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 357 | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8); | 421 | self.mir_instructions.set(jmp_reloc, .{ |
| 358 | if (amt == -4) { | 422 | .tag = .b, |
| 359 | // This return is at the end of the | 423 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, |
| 360 | // code block. We can't just delete | 424 | }); |
| 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 | } | 425 | } |
| 373 | | 426 | |
| 374 | // ldp fp, lr, [sp], #16 | 427 | // ldp fp, lr, [sp], #16 |
| 375 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp( | 428 | _ = try self.addInst(.{ |
| 376 | .x29, | 429 | .tag = .ldp, |
| 377 | .x30, | 430 | .data = .{ .load_store_register_pair = .{ |
| 378 | Register.sp, | 431 | .rt = .x29, |
| 379 | Instruction.LoadStorePairOffset.post_index(16), | 432 | .rt2 = .x30, |
| 380 | ).toU32()); | 433 | .rn = Register.sp, |
| | 434 | .offset = Instruction.LoadStorePairOffset.post_index(16), |
| | 435 | } }, |
| | 436 | }); |
| | 437 | |
| 381 | // add sp, sp, #stack_size | 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 | // ret lr | 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 | } else { | 449 | } else { |
| 386 | try self.dbgSetPrologueEnd(); | 450 | _ = try self.addInst(.{ |
| | 451 | .tag = .dbg_prologue_end, |
| | 452 | .data = .{ .nop = {} }, |
| | 453 | }); |
| | 454 | |
| 387 | try self.genBody(self.air.getMainBody()); | 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 | // Drop them off at the rbrace. | 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 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | 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,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 | /// Asserts there is already capacity to insert into top branch inst_table. | 611 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 607 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 612 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 608 | const air_tags = self.air.instructions.items(.tag); | 613 | const air_tags = self.air.instructions.items(.tag); |
| ... | @@ -1297,310 +1302,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1297,310 +1302,6 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1297 | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); | 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 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { | 1305 | fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1605 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; | 1306 | const ty_str = self.air.instructions.items(.data)[inst].ty_str; |
| 1606 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; | 1307 | const zir = &self.mod_fn.owner_decl.getFileScope().zir; |
| ... | @@ -1652,7 +1353,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1652,7 +1353,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1652 | }, | 1353 | }, |
| 1653 | else => result, | 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 | if (self.liveness.isUnused(inst)) | 1359 | if (self.liveness.isUnused(inst)) |
| 1658 | return self.finishAirBookkeeping(); | 1360 | return self.finishAirBookkeeping(); |
| ... | @@ -1668,7 +1370,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1668,7 +1370,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1668 | } | 1370 | } |
| 1669 | | 1371 | |
| 1670 | fn airBreakpoint(self: *Self) !void { | 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 | return self.finishAirBookkeeping(); | 1377 | return self.finishAirBookkeeping(); |
| 1673 | } | 1378 | } |
| 1674 | | 1379 | |
| ... | @@ -1736,7 +1441,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1736,7 +1441,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1736 | | 1441 | |
| 1737 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); | 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 | } else if (func_value.castTag(.extern_fn)) |_| { | 1448 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1741 | return self.fail("TODO implement calling extern functions", .{}); | 1449 | return self.fail("TODO implement calling extern functions", .{}); |
| 1742 | } else { | 1450 | } else { |
| ... | @@ -1789,25 +1497,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1789,25 +1497,17 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1789 | .memory = func.owner_decl.link.macho.local_sym_index, | 1497 | .memory = func.owner_decl.link.macho.local_sym_index, |
| 1790 | }); | 1498 | }); |
| 1791 | // blr x30 | 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 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | 1504 | } else if (func_value.castTag(.extern_fn)) |func_payload| { |
| 1794 | const decl = func_payload.data; | 1505 | const decl = func_payload.data; |
| 1795 | const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name)); | 1506 | const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name)); |
| 1796 | const offset = blk: { | 1507 | |
| 1797 | const offset = @intCast(u32, self.code.items.len); | 1508 | _ = try self.addInst(.{ |
| 1798 | // bl | 1509 | .tag = .call_extern, |
| 1799 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32()); | 1510 | .data = .{ .extern_fn = n_strx }, |
| 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), | | |
| 1811 | }); | 1511 | }); |
| 1812 | } else { | 1512 | } else { |
| 1813 | return self.fail("TODO implement calling bitcasted functions", .{}); | 1513 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| ... | @@ -1857,7 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1857,7 +1557,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1857 | | 1557 | |
| 1858 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); | 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 | } else if (func_value.castTag(.extern_fn)) |_| { | 1564 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1862 | return self.fail("TODO implement calling extern functions", .{}); | 1565 | return self.fail("TODO implement calling extern functions", .{}); |
| 1863 | } else { | 1566 | } else { |
| ... | @@ -1899,8 +1602,11 @@ fn ret(self: *Self, mcv: MCValue) !void { | ... | @@ -1899,8 +1602,11 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 1899 | const ret_ty = self.fn_type.fnReturnType(); | 1602 | const ret_ty = self.fn_type.fnReturnType(); |
| 1900 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); | 1603 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1901 | // Just add space for an instruction, patch this later | 1604 | // Just add space for an instruction, patch this later |
| 1902 | try self.code.resize(self.code.items.len + 4); | 1605 | const index = try self.addInst(.{ |
| 1903 | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); | 1606 | .tag = .nop, |
| | 1607 | .data = .{ .nop = {} }, |
| | 1608 | }); |
| | 1609 | try self.exitlude_jump_relocs.append(self.gpa, index); |
| 1904 | } | 1610 | } |
| 1905 | | 1611 | |
| 1906 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 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,7 +1645,15 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1939 | | 1645 | |
| 1940 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | 1646 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1941 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; | 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 | return self.finishAirBookkeeping(); | 1657 | return self.finishAirBookkeeping(); |
| 1944 | } | 1658 | } |
| 1945 | | 1659 | |
| ... | @@ -2090,19 +1804,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2090,19 +1804,18 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 2090 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 1804 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2091 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | 1805 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2092 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; | 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 | try self.genBody(body); | 1808 | try self.genBody(body); |
| 2095 | try self.jump(start_index); | 1809 | try self.jump(start_index); |
| 2096 | return self.finishAirBookkeeping(); | 1810 | return self.finishAirBookkeeping(); |
| 2097 | } | 1811 | } |
| 2098 | | 1812 | |
| 2099 | /// Send control flow to the `index` of `self.code`. | 1813 | /// Send control flow to `inst`. |
| 2100 | fn jump(self: *Self, index: usize) !void { | 1814 | 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| { | 1815 | _ = try self.addInst(.{ |
| 2102 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32()); | 1816 | .tag = .b, |
| 2103 | } else |_| { | 1817 | .data = .{ .inst = inst }, |
| 2104 | return self.fail("TODO: enable larger branch offset", .{}); | 1818 | }); |
| 2105 | } | | |
| 2106 | } | 1819 | } |
| 2107 | | 1820 | |
| 2108 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | 1821 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2140,19 +1853,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2140,19 +1853,8 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2140 | | 1853 | |
| 2141 | fn performReloc(self: *Self, reloc: Reloc) !void { | 1854 | fn performReloc(self: *Self, reloc: Reloc) !void { |
| 2142 | switch (reloc) { | 1855 | switch (reloc) { |
| 2143 | .rel32 => |pos| { | 1856 | .rel32 => return self.fail("TODO reloc.rel32 for {}", .{self.target.cpu.arch}), |
| 2144 | const amt = self.code.items.len - (pos + 4); | 1857 | .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 | } | 1858 | } |
| 2157 | } | 1859 | } |
| 2158 | | 1860 | |
| ... | @@ -2244,9 +1946,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2244,9 +1946,15 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2244 | } | 1946 | } |
| 2245 | | 1947 | |
| 2246 | if (mem.eql(u8, asm_source, "svc #0")) { | 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 | } else if (mem.eql(u8, asm_source, "svc #0x80")) { | 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 | } else { | 1958 | } else { |
| 2251 | return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); | 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,6 +2041,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", .{}); | 2041 | return self.fail("TODO implement set stack variable from embedded_in_code", .{}); |
| 2334 | }, | 2042 | }, |
| 2335 | .register => |reg| { | 2043 | .register => |reg| { |
| | 2044 | _ = reg; |
| | 2045 | |
| 2336 | const abi_size = ty.abiSize(self.target.*); | 2046 | const abi_size = ty.abiSize(self.target.*); |
| 2337 | const adj_off = stack_offset + abi_size; | 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,16 +2057,21 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2347 | .aarch64_32 => .w29, | 2057 | .aarch64_32 => .w29, |
| 2348 | else => unreachable, | 2058 | else => unreachable, |
| 2349 | }; | 2059 | }; |
| 2350 | const str = switch (abi_size) { | 2060 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2351 | 1 => Instruction.strb, | 2061 | 1 => .strb, |
| 2352 | 2 => Instruction.strh, | 2062 | 2 => .strh, |
| 2353 | 4, 8 => Instruction.str, | 2063 | 4, 8 => .str, |
| 2354 | else => unreachable, // unexpected abi size | 2064 | else => unreachable, // unexpected abi size |
| 2355 | }; | 2065 | }; |
| 2356 | | 2066 | |
| 2357 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{ | 2067 | _ = try self.addInst(.{ |
| 2358 | .offset = offset, | 2068 | .tag = tag, |
| 2359 | }).toU32()); | 2069 | .data = .{ .load_store_register = .{ |
| | 2070 | .rt = reg, |
| | 2071 | .rn = rn, |
| | 2072 | .offset = offset, |
| | 2073 | } }, |
| | 2074 | }); |
| 2360 | }, | 2075 | }, |
| 2361 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 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,20 +2107,28 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2392 | } | 2107 | } |
| 2393 | }, | 2108 | }, |
| 2394 | .immediate => |x| { | 2109 | .immediate => |x| { |
| 2395 | if (x <= math.maxInt(u16)) { | 2110 | _ = try self.addInst(.{ |
| 2396 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32()); | 2111 | .tag = .movz, |
| 2397 | } else if (x <= math.maxInt(u32)) { | 2112 | .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()); | 2113 | }); |
| 2399 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32()); | 2114 | |
| 2400 | } else if (x <= math.maxInt(u32)) { | 2115 | if (x > math.maxInt(u16)) { |
| 2401 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); | 2116 | _ = try self.addInst(.{ |
| 2402 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); | 2117 | .tag = .movk, |
| 2403 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32()); | 2118 | .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, |
| 2404 | } else { | 2119 | }); |
| 2405 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32()); | 2120 | } |
| 2406 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32()); | 2121 | if (x > math.maxInt(u32)) { |
| 2407 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32()); | 2122 | _ = try self.addInst(.{ |
| 2408 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32()); | 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 | .register => |src_reg| { | 2134 | .register => |src_reg| { |
| ... | @@ -2414,63 +2137,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2414,63 +2137,19 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2414 | return; | 2137 | return; |
| 2415 | | 2138 | |
| 2416 | // mov reg, src_reg | 2139 | // mov reg, src_reg |
| 2417 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr( | 2140 | _ = try self.addInst(.{ |
| 2418 | reg, | 2141 | .tag = .mov_register, |
| 2419 | .xzr, | 2142 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 2420 | src_reg, | 2143 | }); |
| 2421 | Instruction.Shift.none, | | |
| 2422 | ).toU32()); | | |
| 2423 | }, | 2144 | }, |
| 2424 | .memory => |addr| { | 2145 | .memory => |addr| { |
| 2425 | if (self.bin_file.options.pie) { | 2146 | _ = try self.addInst(.{ |
| 2426 | // PC-relative displacement to the entry in the GOT table. | 2147 | .tag = .load_memory, |
| 2427 | // adrp | 2148 | .data = .{ .payload = try self.addExtra(Mir.LoadMemory{ |
| 2428 | const offset = @intCast(u32, self.code.items.len); | 2149 | .register = @enumToInt(reg), |
| 2429 | mem.writeIntLittle( | 2150 | .addr = @intCast(u32, addr), |
| 2430 | u32, | 2151 | }) }, |
| 2431 | try self.code.addManyAsArray(4), | 2152 | }); |
| 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 | } | | |
| 2474 | }, | 2153 | }, |
| 2475 | .stack_offset => |unadjusted_off| { | 2154 | .stack_offset => |unadjusted_off| { |
| 2476 | // TODO: maybe addressing from sp instead of fp | 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,22 +2168,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 })); | 2168 | Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off })); |
| 2490 | | 2169 | |
| 2491 | switch (abi_size) { | 2170 | switch (abi_size) { |
| 2492 | 1, 2 => { | 2171 | 1, 2, 4, 8 => { |
| 2493 | const ldr = switch (abi_size) { | 2172 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2494 | 1 => Instruction.ldrb, | 2173 | 1 => .ldrb, |
| 2495 | 2 => Instruction.ldrh, | 2174 | 2 => .ldrh, |
| | 2175 | 4, 8 => .ldr, |
| 2496 | else => unreachable, // unexpected abi size | 2176 | else => unreachable, // unexpected abi size |
| 2497 | }; | 2177 | }; |
| 2498 | | 2178 | |
| 2499 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{ | 2179 | _ = try self.addInst(.{ |
| 2500 | .offset = offset, | 2180 | .tag = tag, |
| 2501 | }).toU32()); | 2181 | .data = .{ .load_store_register = .{ |
| 2502 | }, | 2182 | .rt = reg, |
| 2503 | 4, 8 => { | 2183 | .rn = rn, |
| 2504 | mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ | 2184 | .offset = offset, |
| 2505 | .rn = rn, | 2185 | } }, |
| 2506 | .offset = offset, | 2186 | }); |
| 2507 | } }).toU32()); | | |
| 2508 | }, | 2187 | }, |
| 2509 | else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), | 2188 | else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}), |
| 2510 | } | 2189 | } |