| ... | @@ -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; |
| ... | @@ -22,9 +24,9 @@ const log = std.log.scoped(.codegen); | ... | @@ -22,9 +24,9 @@ const log = std.log.scoped(.codegen); |
| 22 | const build_options = @import("build_options"); | 24 | const build_options = @import("build_options"); |
| 23 | const RegisterManager = @import("../../register_manager.zig").RegisterManager; | 25 | const RegisterManager = @import("../../register_manager.zig").RegisterManager; |
| 24 | | 26 | |
| 25 | pub const FnResult = @import("../../codegen.zig").FnResult; | 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 26 | pub const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 27 | pub const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 29 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 28 | | 30 | |
| 29 | const InnerError = error{ | 31 | const InnerError = error{ |
| 30 | OutOfMemory, | 32 | OutOfMemory, |
| ... | @@ -37,8 +39,6 @@ liveness: Liveness, | ... | @@ -37,8 +39,6 @@ liveness: Liveness, |
| 37 | bin_file: *link.File, | 39 | bin_file: *link.File, |
| 38 | target: *const std.Target, | 40 | target: *const std.Target, |
| 39 | mod_fn: *const Module.Fn, | 41 | mod_fn: *const Module.Fn, |
| 40 | code: *std.ArrayList(u8), | | |
| 41 | debug_output: DebugInfoOutput, | | |
| 42 | err_msg: ?*ErrorMsg, | 42 | err_msg: ?*ErrorMsg, |
| 43 | args: []MCValue, | 43 | args: []MCValue, |
| 44 | ret_mcv: MCValue, | 44 | ret_mcv: MCValue, |
| ... | @@ -47,13 +47,14 @@ arg_index: usize, | ... | @@ -47,13 +47,14 @@ arg_index: usize, |
| 47 | src_loc: Module.SrcLoc, | 47 | src_loc: Module.SrcLoc, |
| 48 | stack_align: u32, | 48 | stack_align: u32, |
| 49 | | 49 | |
| 50 | prev_di_line: u32, | 50 | /// MIR Instructions |
| 51 | prev_di_column: u32, | 51 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| | 52 | /// MIR extra data |
| | 53 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| | 54 | |
| 52 | /// Byte offset within the source file of the ending curly. | 55 | /// Byte offset within the source file of the ending curly. |
| 53 | end_di_line: u32, | 56 | end_di_line: u32, |
| 54 | end_di_column: u32, | 57 | end_di_column: u32, |
| 55 | /// Relative to the beginning of `code`. | | |
| 56 | prev_di_pc: usize, | | |
| 57 | | 58 | |
| 58 | /// 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. |
| 59 | /// To perform the reloc, write 32-bit signed little-endian integer | 60 | /// To perform the reloc, write 32-bit signed little-endian integer |
| ... | @@ -176,7 +177,7 @@ const StackAllocation = struct { | ... | @@ -176,7 +177,7 @@ const StackAllocation = struct { |
| 176 | }; | 177 | }; |
| 177 | | 178 | |
| 178 | const BlockData = struct { | 179 | const BlockData = struct { |
| 179 | relocs: std.ArrayListUnmanaged(Reloc), | 180 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index), |
| 180 | /// The first break instruction encounters `null` here and chooses a | 181 | /// The first break instruction encounters `null` here and chooses a |
| 181 | /// machine code value for the block result, populating this field. | 182 | /// machine code value for the block result, populating this field. |
| 182 | /// Following break instructions encounter that value and use it for | 183 | /// Following break instructions encounter that value and use it for |
| ... | @@ -184,18 +185,6 @@ const BlockData = struct { | ... | @@ -184,18 +185,6 @@ const BlockData = struct { |
| 184 | mcv: MCValue, | 185 | mcv: MCValue, |
| 185 | }; | 186 | }; |
| 186 | | 187 | |
| 187 | const Reloc = union(enum) { | | |
| 188 | /// The value is an offset into the `Function` `code` from the beginning. | | |
| 189 | /// To perform the reloc, write 32-bit signed little-endian integer | | |
| 190 | /// which is a relative jump, based on the address following the reloc. | | |
| 191 | rel32: usize, | | |
| 192 | /// A branch in the ARM instruction set | | |
| 193 | arm_branch: struct { | | |
| 194 | pos: usize, | | |
| 195 | cond: @import("bits.zig").Condition, | | |
| 196 | }, | | |
| 197 | }; | | |
| 198 | | | |
| 199 | const BigTomb = struct { | 188 | const BigTomb = struct { |
| 200 | function: *Self, | 189 | function: *Self, |
| 201 | inst: Air.Inst.Index, | 190 | inst: Air.Inst.Index, |
| ... | @@ -266,8 +255,6 @@ pub fn generate( | ... | @@ -266,8 +255,6 @@ pub fn generate( |
| 266 | .target = &bin_file.options.target, | 255 | .target = &bin_file.options.target, |
| 267 | .bin_file = bin_file, | 256 | .bin_file = bin_file, |
| 268 | .mod_fn = module_fn, | 257 | .mod_fn = module_fn, |
| 269 | .code = code, | | |
| 270 | .debug_output = debug_output, | | |
| 271 | .err_msg = null, | 258 | .err_msg = null, |
| 272 | .args = undefined, // populated after `resolveCallingConventionValues` | 259 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 273 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` | 260 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| ... | @@ -276,9 +263,6 @@ pub fn generate( | ... | @@ -276,9 +263,6 @@ pub fn generate( |
| 276 | .branch_stack = &branch_stack, | 263 | .branch_stack = &branch_stack, |
| 277 | .src_loc = src_loc, | 264 | .src_loc = src_loc, |
| 278 | .stack_align = undefined, | 265 | .stack_align = undefined, |
| 279 | .prev_di_pc = 0, | | |
| 280 | .prev_di_line = module_fn.lbrace_line, | | |
| 281 | .prev_di_column = module_fn.lbrace_column, | | |
| 282 | .end_di_line = module_fn.rbrace_line, | 266 | .end_di_line = module_fn.rbrace_line, |
| 283 | .end_di_column = module_fn.rbrace_column, | 267 | .end_di_column = module_fn.rbrace_column, |
| 284 | }; | 268 | }; |
| ... | @@ -302,6 +286,30 @@ pub fn generate( | ... | @@ -302,6 +286,30 @@ pub fn generate( |
| 302 | else => |e| return e, | 286 | else => |e| return e, |
| 303 | }; | 287 | }; |
| 304 | | 288 | |
| | 289 | var mir = Mir{ |
| | 290 | .instructions = function.mir_instructions.toOwnedSlice(), |
| | 291 | .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), |
| | 292 | }; |
| | 293 | defer mir.deinit(bin_file.allocator); |
| | 294 | |
| | 295 | var emit = Emit{ |
| | 296 | .mir = mir, |
| | 297 | .bin_file = bin_file, |
| | 298 | .debug_output = debug_output, |
| | 299 | .target = &bin_file.options.target, |
| | 300 | .src_loc = src_loc, |
| | 301 | .code = code, |
| | 302 | .prev_di_pc = 0, |
| | 303 | .prev_di_line = module_fn.lbrace_line, |
| | 304 | .prev_di_column = module_fn.lbrace_column, |
| | 305 | }; |
| | 306 | defer emit.deinit(); |
| | 307 | |
| | 308 | emit.emitMir() catch |err| switch (err) { |
| | 309 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, |
| | 310 | else => |e| return e, |
| | 311 | }; |
| | 312 | |
| 305 | if (function.err_msg) |em| { | 313 | if (function.err_msg) |em| { |
| 306 | return FnResult{ .fail = em }; | 314 | return FnResult{ .fail = em }; |
| 307 | } else { | 315 | } else { |
| ... | @@ -309,17 +317,68 @@ pub fn generate( | ... | @@ -309,17 +317,68 @@ pub fn generate( |
| 309 | } | 317 | } |
| 310 | } | 318 | } |
| 311 | | 319 | |
| | 320 | fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| | 321 | const gpa = self.gpa; |
| | 322 | |
| | 323 | try self.mir_instructions.ensureUnusedCapacity(gpa, 1); |
| | 324 | |
| | 325 | const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); |
| | 326 | self.mir_instructions.appendAssumeCapacity(inst); |
| | 327 | return result_index; |
| | 328 | } |
| | 329 | |
| | 330 | fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { |
| | 331 | return try self.addInst(.{ |
| | 332 | .tag = .nop, |
| | 333 | .cond = .al, |
| | 334 | .data = .{ .nop = {} }, |
| | 335 | }); |
| | 336 | } |
| | 337 | |
| | 338 | pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { |
| | 339 | const fields = std.meta.fields(@TypeOf(extra)); |
| | 340 | try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); |
| | 341 | return self.addExtraAssumeCapacity(extra); |
| | 342 | } |
| | 343 | |
| | 344 | pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| | 345 | const fields = std.meta.fields(@TypeOf(extra)); |
| | 346 | const result = @intCast(u32, self.mir_extra.items.len); |
| | 347 | inline for (fields) |field| { |
| | 348 | self.mir_extra.appendAssumeCapacity(switch (field.field_type) { |
| | 349 | u32 => @field(extra, field.name), |
| | 350 | i32 => @bitCast(u32, @field(extra, field.name)), |
| | 351 | else => @compileError("bad field type"), |
| | 352 | }); |
| | 353 | } |
| | 354 | return result; |
| | 355 | } |
| | 356 | |
| 312 | fn gen(self: *Self) !void { | 357 | fn gen(self: *Self) !void { |
| 313 | const cc = self.fn_type.fnCallingConvention(); | 358 | const cc = self.fn_type.fnCallingConvention(); |
| 314 | if (cc != .Naked) { | 359 | if (cc != .Naked) { |
| 315 | // push {fp, lr} | 360 | // push {fp, lr} |
| | 361 | const push_reloc = try self.addNop(); |
| | 362 | |
| 316 | // mov fp, sp | 363 | // mov fp, sp |
| | 364 | _ = try self.addInst(.{ |
| | 365 | .tag = .mov, |
| | 366 | .cond = .al, |
| | 367 | .data = .{ .rr_op = .{ |
| | 368 | .rd = .fp, |
| | 369 | .rn = .r0, |
| | 370 | .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none), |
| | 371 | } }, |
| | 372 | }); |
| | 373 | |
| 317 | // sub sp, sp, #reloc | 374 | // sub sp, sp, #reloc |
| 318 | const prologue_reloc = self.code.items.len; | 375 | const sub_reloc = try self.addNop(); |
| 319 | try self.code.resize(prologue_reloc + 12); | | |
| 320 | self.writeInt(u32, self.code.items[prologue_reloc + 4 ..][0..4], Instruction.mov(.al, .fp, Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none)).toU32()); | | |
| 321 | | 376 | |
| 322 | try self.dbgSetPrologueEnd(); | 377 | _ = try self.addInst(.{ |
| | 378 | .tag = .dbg_prologue_end, |
| | 379 | .cond = undefined, |
| | 380 | .data = .{ .nop = {} }, |
| | 381 | }); |
| 323 | | 382 | |
| 324 | try self.genBody(self.air.getMainBody()); | 383 | try self.genBody(self.air.getMainBody()); |
| 325 | | 384 | |
| ... | @@ -333,18 +392,30 @@ fn gen(self: *Self) !void { | ... | @@ -333,18 +392,30 @@ fn gen(self: *Self) !void { |
| 333 | @field(saved_regs, @tagName(reg)) = true; | 392 | @field(saved_regs, @tagName(reg)) = true; |
| 334 | } | 393 | } |
| 335 | } | 394 | } |
| 336 | self.writeInt(u32, self.code.items[prologue_reloc..][0..4], Instruction.stmdb(.al, .sp, true, saved_regs).toU32()); | 395 | self.mir_instructions.set(push_reloc, .{ |
| | 396 | .tag = .push, |
| | 397 | .cond = .al, |
| | 398 | .data = .{ .register_list = saved_regs }, |
| | 399 | }); |
| 337 | | 400 | |
| 338 | // Backpatch stack offset | 401 | // Backpatch stack offset |
| 339 | const stack_end = self.max_end_stack; | 402 | const stack_end = self.max_end_stack; |
| 340 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); | 403 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 341 | if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| { | 404 | if (Instruction.Operand.fromU32(@intCast(u32, aligned_stack_end))) |op| { |
| 342 | self.writeInt(u32, self.code.items[prologue_reloc + 8 ..][0..4], Instruction.sub(.al, .sp, .sp, op).toU32()); | 405 | self.mir_instructions.set(sub_reloc, .{ |
| | 406 | .tag = .sub, |
| | 407 | .cond = .al, |
| | 408 | .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } }, |
| | 409 | }); |
| 343 | } else { | 410 | } else { |
| 344 | return self.failSymbol("TODO ARM: allow larger stacks", .{}); | 411 | return self.failSymbol("TODO ARM: allow larger stacks", .{}); |
| 345 | } | 412 | } |
| 346 | | 413 | |
| 347 | try self.dbgSetEpilogueBegin(); | 414 | _ = try self.addInst(.{ |
| | 415 | .tag = .dbg_epilogue_begin, |
| | 416 | .cond = undefined, |
| | 417 | .data = .{ .nop = {} }, |
| | 418 | }); |
| 348 | | 419 | |
| 349 | // exitlude jumps | 420 | // exitlude jumps |
| 350 | if (self.exitlude_jump_relocs.items.len == 1) { | 421 | if (self.exitlude_jump_relocs.items.len == 1) { |
| ... | @@ -353,23 +424,13 @@ fn gen(self: *Self) !void { | ... | @@ -353,23 +424,13 @@ fn gen(self: *Self) !void { |
| 353 | // the code. Therefore, we can just delete | 424 | // the code. Therefore, we can just delete |
| 354 | // the space initially reserved for the | 425 | // the space initially reserved for the |
| 355 | // jump | 426 | // jump |
| 356 | self.code.items.len -= 4; | 427 | self.mir_instructions.len -= 1; |
| 357 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { | 428 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 358 | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8); | 429 | self.mir_instructions.set(jmp_reloc, .{ |
| 359 | if (amt == -4) { | 430 | .tag = .b, |
| 360 | // This return is at the end of the | 431 | .cond = .al, |
| 361 | // code block. We can't just delete | 432 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, |
| 362 | // the space because there may be | 433 | }); |
| 363 | // other jumps we already relocated to | | |
| 364 | // the address. Instead, insert a nop | | |
| 365 | self.writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32()); | | |
| 366 | } else { | | |
| 367 | if (math.cast(i26, amt)) |offset| { | | |
| 368 | self.writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(.al, offset).toU32()); | | |
| 369 | } else |_| { | | |
| 370 | return self.failSymbol("exitlude jump is too large", .{}); | | |
| 371 | } | | |
| 372 | } | | |
| 373 | } | 434 | } |
| 374 | | 435 | |
| 375 | // Epilogue: pop callee saved registers (swap lr with pc in saved_regs) | 436 | // Epilogue: pop callee saved registers (swap lr with pc in saved_regs) |
| ... | @@ -377,17 +438,47 @@ fn gen(self: *Self) !void { | ... | @@ -377,17 +438,47 @@ fn gen(self: *Self) !void { |
| 377 | saved_regs.r15 = true; // pc | 438 | saved_regs.r15 = true; // pc |
| 378 | | 439 | |
| 379 | // mov sp, fp | 440 | // mov sp, fp |
| | 441 | _ = try self.addInst(.{ |
| | 442 | .tag = .mov, |
| | 443 | .cond = .al, |
| | 444 | .data = .{ .rr_op = .{ |
| | 445 | .rd = .sp, |
| | 446 | .rn = .r0, |
| | 447 | .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none), |
| | 448 | } }, |
| | 449 | }); |
| | 450 | |
| 380 | // pop {fp, pc} | 451 | // pop {fp, pc} |
| 381 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .sp, Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none)).toU32()); | 452 | _ = try self.addInst(.{ |
| 382 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32()); | 453 | .tag = .pop, |
| | 454 | .cond = .al, |
| | 455 | .data = .{ .register_list = saved_regs }, |
| | 456 | }); |
| 383 | } else { | 457 | } else { |
| 384 | try self.dbgSetPrologueEnd(); | 458 | _ = try self.addInst(.{ |
| | 459 | .tag = .dbg_prologue_end, |
| | 460 | .cond = undefined, |
| | 461 | .data = .{ .nop = {} }, |
| | 462 | }); |
| | 463 | |
| 385 | try self.genBody(self.air.getMainBody()); | 464 | try self.genBody(self.air.getMainBody()); |
| 386 | try self.dbgSetEpilogueBegin(); | 465 | |
| | 466 | _ = try self.addInst(.{ |
| | 467 | .tag = .dbg_epilogue_begin, |
| | 468 | .cond = undefined, |
| | 469 | .data = .{ .nop = {} }, |
| | 470 | }); |
| 387 | } | 471 | } |
| 388 | | 472 | |
| 389 | // Drop them off at the rbrace. | 473 | // Drop them off at the rbrace. |
| 390 | try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column); | 474 | _ = try self.addInst(.{ |
| | 475 | .tag = .dbg_line, |
| | 476 | .cond = undefined, |
| | 477 | .data = .{ .dbg_line_column = .{ |
| | 478 | .line = self.end_di_line, |
| | 479 | .column = self.end_di_column, |
| | 480 | } }, |
| | 481 | }); |
| 391 | } | 482 | } |
| 392 | | 483 | |
| 393 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | 484 | fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| ... | @@ -534,79 +625,6 @@ fn writeInt(self: *Self, comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bit | ... | @@ -534,79 +625,6 @@ fn writeInt(self: *Self, comptime T: type, buf: *[@divExact(@typeInfo(T).Int.bit |
| 534 | std.mem.writeInt(T, buf, value, endian); | 625 | std.mem.writeInt(T, buf, value, endian); |
| 535 | } | 626 | } |
| 536 | | 627 | |
| 537 | fn dbgSetPrologueEnd(self: *Self) InnerError!void { | | |
| 538 | switch (self.debug_output) { | | |
| 539 | .dwarf => |dbg_out| { | | |
| 540 | try dbg_out.dbg_line.append(DW.LNS.set_prologue_end); | | |
| 541 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); | | |
| 542 | }, | | |
| 543 | .plan9 => {}, | | |
| 544 | .none => {}, | | |
| 545 | } | | |
| 546 | } | | |
| 547 | | | |
| 548 | fn dbgSetEpilogueBegin(self: *Self) InnerError!void { | | |
| 549 | switch (self.debug_output) { | | |
| 550 | .dwarf => |dbg_out| { | | |
| 551 | try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin); | | |
| 552 | try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column); | | |
| 553 | }, | | |
| 554 | .plan9 => {}, | | |
| 555 | .none => {}, | | |
| 556 | } | | |
| 557 | } | | |
| 558 | | | |
| 559 | fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void { | | |
| 560 | const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line); | | |
| 561 | const delta_pc: usize = self.code.items.len - self.prev_di_pc; | | |
| 562 | switch (self.debug_output) { | | |
| 563 | .dwarf => |dbg_out| { | | |
| 564 | // TODO Look into using the DWARF special opcodes to compress this data. | | |
| 565 | // It lets you emit single-byte opcodes that add different numbers to | | |
| 566 | // both the PC and the line number at the same time. | | |
| 567 | try dbg_out.dbg_line.ensureUnusedCapacity(11); | | |
| 568 | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc); | | |
| 569 | leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable; | | |
| 570 | if (delta_line != 0) { | | |
| 571 | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line); | | |
| 572 | leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable; | | |
| 573 | } | | |
| 574 | dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy); | | |
| 575 | self.prev_di_pc = self.code.items.len; | | |
| 576 | self.prev_di_line = line; | | |
| 577 | self.prev_di_column = column; | | |
| 578 | self.prev_di_pc = self.code.items.len; | | |
| 579 | }, | | |
| 580 | .plan9 => |dbg_out| { | | |
| 581 | if (delta_pc <= 0) return; // only do this when the pc changes | | |
| 582 | // we have already checked the target in the linker to make sure it is compatable | | |
| 583 | const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable; | | |
| 584 | | | |
| 585 | // increasing the line number | | |
| 586 | try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); | | |
| 587 | // increasing the pc | | |
| 588 | const d_pc_p9 = @intCast(i64, delta_pc) - quant; | | |
| 589 | if (d_pc_p9 > 0) { | | |
| 590 | // minus one because if its the last one, we want to leave space to change the line which is one quanta | | |
| 591 | try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant); | | |
| 592 | if (dbg_out.pcop_change_index.*) |pci| | | |
| 593 | dbg_out.dbg_line.items[pci] += 1; | | |
| 594 | dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1); | | |
| 595 | } else if (d_pc_p9 == 0) { | | |
| 596 | // we don't need to do anything, because adding the quant does it for us | | |
| 597 | } else unreachable; | | |
| 598 | if (dbg_out.start_line.* == null) | | |
| 599 | dbg_out.start_line.* = self.prev_di_line; | | |
| 600 | dbg_out.end_line.* = line; | | |
| 601 | // only do this if the pc changed | | |
| 602 | self.prev_di_line = line; | | |
| 603 | self.prev_di_column = column; | | |
| 604 | self.prev_di_pc = self.code.items.len; | | |
| 605 | }, | | |
| 606 | .none => {}, | | |
| 607 | } | | |
| 608 | } | | |
| 609 | | | |
| 610 | /// Asserts there is already capacity to insert into top branch inst_table. | 628 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 611 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { | 629 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 612 | const air_tags = self.air.instructions.items(.tag); | 630 | const air_tags = self.air.instructions.items(.tag); |
| ... | @@ -1217,7 +1235,15 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1217,7 +1235,15 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1217 | .compare_flags_signed, .compare_flags_unsigned => unreachable, | 1235 | .compare_flags_signed, .compare_flags_unsigned => unreachable, |
| 1218 | .embedded_in_code => unreachable, | 1236 | .embedded_in_code => unreachable, |
| 1219 | .register => |dst_reg| { | 1237 | .register => |dst_reg| { |
| 1220 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, dst_reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); | 1238 | _ = try self.addInst(.{ |
| | 1239 | .tag = .ldr, |
| | 1240 | .cond = .al, |
| | 1241 | .data = .{ .rr_offset = .{ |
| | 1242 | .rt = dst_reg, |
| | 1243 | .rn = reg, |
| | 1244 | .offset = .{ .offset = Instruction.Offset.none }, |
| | 1245 | } }, |
| | 1246 | }); |
| 1221 | }, | 1247 | }, |
| 1222 | else => return self.fail("TODO load from register into {}", .{dst_mcv}), | 1248 | else => return self.fail("TODO load from register into {}", .{dst_mcv}), |
| 1223 | } | 1249 | } |
| ... | @@ -1513,48 +1539,81 @@ fn genArmBinOpCode( | ... | @@ -1513,48 +1539,81 @@ fn genArmBinOpCode( |
| 1513 | }; | 1539 | }; |
| 1514 | | 1540 | |
| 1515 | switch (op) { | 1541 | switch (op) { |
| 1516 | .add => { | 1542 | .add, |
| 1517 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32()); | 1543 | .bool_and, |
| | 1544 | .bit_and, |
| | 1545 | .bool_or, |
| | 1546 | .bit_or, |
| | 1547 | .not, |
| | 1548 | .xor, |
| | 1549 | => { |
| | 1550 | const tag: Mir.Inst.Tag = switch (op) { |
| | 1551 | .add => .add, |
| | 1552 | .bool_and, .bit_and => .@"and", |
| | 1553 | .bool_or, .bit_or => .orr, |
| | 1554 | .not, .xor => .eor, |
| | 1555 | else => unreachable, |
| | 1556 | }; |
| | 1557 | |
| | 1558 | _ = try self.addInst(.{ |
| | 1559 | .tag = tag, |
| | 1560 | .cond = .al, |
| | 1561 | .data = .{ .rr_op = .{ |
| | 1562 | .rd = dst_reg, |
| | 1563 | .rn = op1, |
| | 1564 | .op = operand, |
| | 1565 | } }, |
| | 1566 | }); |
| 1518 | }, | 1567 | }, |
| 1519 | .sub => { | 1568 | .sub => { |
| 1520 | if (swap_lhs_and_rhs) { | 1569 | const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub; |
| 1521 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32()); | 1570 | |
| 1522 | } else { | 1571 | _ = try self.addInst(.{ |
| 1523 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32()); | 1572 | .tag = tag, |
| 1524 | } | 1573 | .cond = .al, |
| 1525 | }, | 1574 | .data = .{ .rr_op = .{ |
| 1526 | .bool_and, .bit_and => { | 1575 | .rd = dst_reg, |
| 1527 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32()); | 1576 | .rn = op1, |
| 1528 | }, | 1577 | .op = operand, |
| 1529 | .bool_or, .bit_or => { | 1578 | } }, |
| 1530 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32()); | 1579 | }); |
| 1531 | }, | | |
| 1532 | .not, .xor => { | | |
| 1533 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32()); | | |
| 1534 | }, | 1580 | }, |
| 1535 | .cmp_eq => { | 1581 | .cmp_eq => { |
| 1536 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32()); | 1582 | _ = try self.addInst(.{ |
| | 1583 | .tag = .cmp, |
| | 1584 | .cond = .al, |
| | 1585 | .data = .{ .rr_op = .{ |
| | 1586 | .rd = .r0, |
| | 1587 | .rn = op1, |
| | 1588 | .op = operand, |
| | 1589 | } }, |
| | 1590 | }); |
| 1537 | }, | 1591 | }, |
| 1538 | .shl => { | 1592 | .shl, .shr => { |
| 1539 | assert(!swap_lhs_and_rhs); | | |
| 1540 | const shift_amount = switch (operand) { | | |
| 1541 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | | |
| 1542 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | | |
| 1543 | }; | | |
| 1544 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32()); | | |
| 1545 | }, | | |
| 1546 | .shr => { | | |
| 1547 | assert(!swap_lhs_and_rhs); | 1593 | assert(!swap_lhs_and_rhs); |
| 1548 | const shift_amount = switch (operand) { | 1594 | const shift_amount = switch (operand) { |
| 1549 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | 1595 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1550 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | 1596 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1551 | }; | 1597 | }; |
| 1552 | | 1598 | |
| 1553 | const shr = switch (signedness) { | 1599 | const tag: Mir.Inst.Tag = switch (op) { |
| 1554 | .signed => Instruction.asr, | 1600 | .shl => .lsl, |
| 1555 | .unsigned => Instruction.lsr, | 1601 | .shr => switch (signedness) { |
| | 1602 | .signed => Mir.Inst.Tag.asr, |
| | 1603 | .unsigned => Mir.Inst.Tag.lsr, |
| | 1604 | }, |
| | 1605 | else => unreachable, |
| 1556 | }; | 1606 | }; |
| 1557 | self.writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32()); | 1607 | |
| | 1608 | _ = try self.addInst(.{ |
| | 1609 | .tag = tag, |
| | 1610 | .cond = .al, |
| | 1611 | .data = .{ .rr_shift = .{ |
| | 1612 | .rd = dst_reg, |
| | 1613 | .rm = op1, |
| | 1614 | .shift_amount = shift_amount, |
| | 1615 | } }, |
| | 1616 | }); |
| 1558 | }, | 1617 | }, |
| 1559 | else => unreachable, // not a binary instruction | 1618 | else => unreachable, // not a binary instruction |
| 1560 | } | 1619 | } |
| ... | @@ -1623,7 +1682,15 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai | ... | @@ -1623,7 +1682,15 @@ fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Ai |
| 1623 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); | 1682 | try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs); |
| 1624 | } | 1683 | } |
| 1625 | | 1684 | |
| 1626 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32()); | 1685 | _ = try self.addInst(.{ |
| | 1686 | .tag = .mul, |
| | 1687 | .cond = .al, |
| | 1688 | .data = .{ .rrr = .{ |
| | 1689 | .rd = dst_mcv.register, |
| | 1690 | .rn = lhs_mcv.register, |
| | 1691 | .rm = rhs_mcv.register, |
| | 1692 | } }, |
| | 1693 | }); |
| 1627 | return dst_mcv; | 1694 | return dst_mcv; |
| 1628 | } | 1695 | } |
| 1629 | | 1696 | |
| ... | @@ -1707,7 +1774,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1707,7 +1774,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1707 | }, | 1774 | }, |
| 1708 | else => result, | 1775 | else => result, |
| 1709 | }; | 1776 | }; |
| 1710 | try self.genArgDbgInfo(inst, mcv); | 1777 | // TODO generate debug info |
| | 1778 | // try self.genArgDbgInfo(inst, mcv); |
| 1711 | | 1779 | |
| 1712 | if (self.liveness.isUnused(inst)) | 1780 | if (self.liveness.isUnused(inst)) |
| 1713 | return self.finishAirBookkeeping(); | 1781 | return self.finishAirBookkeeping(); |
| ... | @@ -1723,7 +1791,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1723,7 +1791,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1723 | } | 1791 | } |
| 1724 | | 1792 | |
| 1725 | fn airBreakpoint(self: *Self) !void { | 1793 | fn airBreakpoint(self: *Self) !void { |
| 1726 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32()); | 1794 | _ = try self.addInst(.{ |
| | 1795 | .tag = .bkpt, |
| | 1796 | .cond = .al, |
| | 1797 | .data = .{ .imm16 = 0 }, |
| | 1798 | }); |
| 1727 | return self.finishAirBookkeeping(); | 1799 | return self.finishAirBookkeeping(); |
| 1728 | } | 1800 | } |
| 1729 | | 1801 | |
| ... | @@ -1794,10 +1866,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1794,10 +1866,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1794 | // TODO: add Instruction.supportedOn | 1866 | // TODO: add Instruction.supportedOn |
| 1795 | // function for ARM | 1867 | // function for ARM |
| 1796 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { | 1868 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { |
| 1797 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.blx(.al, .lr).toU32()); | 1869 | _ = try self.addInst(.{ |
| | 1870 | .tag = .blx, |
| | 1871 | .cond = .al, |
| | 1872 | .data = .{ .reg = .lr }, |
| | 1873 | }); |
| 1798 | } else { | 1874 | } else { |
| 1799 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32()); | 1875 | return self.fail("TODO fix blx emulatio for ARM <v5", .{}); |
| 1800 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); | 1876 | // _ = try self.addInst(.{ |
| | 1877 | // .tag = .mov, |
| | 1878 | // .cond = .al, |
| | 1879 | // .data = .{ .rr_op = .{ |
| | 1880 | // .rd = .lr, |
| | 1881 | // .rn = .r0, |
| | 1882 | // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none), |
| | 1883 | // } }, |
| | 1884 | // }); |
| | 1885 | // _ = try self.addInst(.{ |
| | 1886 | // .tag = .bx, |
| | 1887 | // .cond = .al, |
| | 1888 | // .data = .{ .reg = .lr }, |
| | 1889 | // }); |
| 1801 | } | 1890 | } |
| 1802 | } else if (func_value.castTag(.extern_fn)) |_| { | 1891 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1803 | return self.fail("TODO implement calling extern functions", .{}); | 1892 | return self.fail("TODO implement calling extern functions", .{}); |
| ... | @@ -1845,8 +1934,7 @@ fn ret(self: *Self, mcv: MCValue) !void { | ... | @@ -1845,8 +1934,7 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 1845 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); | 1934 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1846 | | 1935 | |
| 1847 | // Just add space for an instruction, patch this later | 1936 | // Just add space for an instruction, patch this later |
| 1848 | try self.code.resize(self.code.items.len + 4); | 1937 | try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); |
| 1849 | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); | | |
| 1850 | } | 1938 | } |
| 1851 | | 1939 | |
| 1852 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { | 1940 | fn airRet(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1925,7 +2013,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -1925,7 +2013,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1925 | | 2013 | |
| 1926 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { | 2014 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1927 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; | 2015 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 1928 | try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column); | 2016 | |
| | 2017 | _ = try self.addInst(.{ |
| | 2018 | .tag = .dbg_line, |
| | 2019 | .cond = undefined, |
| | 2020 | .data = .{ .dbg_line_column = .{ |
| | 2021 | .line = dbg_stmt.line, |
| | 2022 | .column = dbg_stmt.column, |
| | 2023 | } }, |
| | 2024 | }); |
| | 2025 | |
| 1929 | return self.finishAirBookkeeping(); | 2026 | return self.finishAirBookkeeping(); |
| 1930 | } | 2027 | } |
| 1931 | | 2028 | |
| ... | @@ -1937,7 +2034,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1937,7 +2034,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1937 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; | 2034 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 1938 | const liveness_condbr = self.liveness.getCondBr(inst); | 2035 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 1939 | | 2036 | |
| 1940 | const reloc: Reloc = reloc: { | 2037 | const reloc: Mir.Inst.Index = reloc: { |
| 1941 | const condition: Condition = switch (cond) { | 2038 | const condition: Condition = switch (cond) { |
| 1942 | .compare_flags_signed => |cmp_op| blk: { | 2039 | .compare_flags_signed => |cmp_op| blk: { |
| 1943 | // Here we map to the opposite condition because the jump is to the false branch. | 2040 | // Here we map to the opposite condition because the jump is to the false branch. |
| ... | @@ -1952,21 +2049,26 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1952,21 +2049,26 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1952 | .register => |reg| blk: { | 2049 | .register => |reg| blk: { |
| 1953 | // cmp reg, 1 | 2050 | // cmp reg, 1 |
| 1954 | // bne ... | 2051 | // bne ... |
| 1955 | const op = Instruction.Operand.imm(1, 0); | 2052 | _ = try self.addInst(.{ |
| 1956 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); | 2053 | .tag = .cmp, |
| | 2054 | .cond = .al, |
| | 2055 | .data = .{ .rr_op = .{ |
| | 2056 | .rd = .r0, |
| | 2057 | .rn = reg, |
| | 2058 | .op = Instruction.Operand.imm(1, 0), |
| | 2059 | } }, |
| | 2060 | }); |
| | 2061 | |
| 1957 | break :blk .ne; | 2062 | break :blk .ne; |
| 1958 | }, | 2063 | }, |
| 1959 | else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), | 2064 | else => return self.fail("TODO implement condbr {} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), |
| 1960 | }; | 2065 | }; |
| 1961 | | 2066 | |
| 1962 | const reloc = Reloc{ | 2067 | break :reloc try self.addInst(.{ |
| 1963 | .arm_branch = .{ | 2068 | .tag = .b, |
| 1964 | .pos = self.code.items.len, | 2069 | .cond = condition, |
| 1965 | .cond = condition, | 2070 | .data = .{ .inst = undefined }, // populated later through performReloc |
| 1966 | }, | 2071 | }); |
| 1967 | }; | | |
| 1968 | try self.code.resize(self.code.items.len + 4); | | |
| 1969 | break :reloc reloc; | | |
| 1970 | }; | 2072 | }; |
| 1971 | | 2073 | |
| 1972 | // Capture the state of register and stack allocation state so that we can revert to it. | 2074 | // Capture the state of register and stack allocation state so that we can revert to it. |
| ... | @@ -2225,19 +2327,19 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2225,19 +2327,19 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 2225 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2327 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2226 | const loop = self.air.extraData(Air.Block, ty_pl.payload); | 2328 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2227 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; | 2329 | const body = self.air.extra[loop.end..][0..loop.data.body_len]; |
| 2228 | const start_index = self.code.items.len; | 2330 | const start_index = @intCast(Mir.Inst.Index, self.mir_instructions.len); |
| 2229 | try self.genBody(body); | 2331 | try self.genBody(body); |
| 2230 | try self.jump(start_index); | 2332 | try self.jump(start_index); |
| 2231 | return self.finishAirBookkeeping(); | 2333 | return self.finishAirBookkeeping(); |
| 2232 | } | 2334 | } |
| 2233 | | 2335 | |
| 2234 | /// Send control flow to the `index` of `self.code`. | 2336 | /// Send control flow to `inst`. |
| 2235 | fn jump(self: *Self, index: usize) !void { | 2337 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { |
| 2236 | if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { | 2338 | _ = try self.addInst(.{ |
| 2237 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32()); | 2339 | .tag = .b, |
| 2238 | } else |_| { | 2340 | .cond = .al, |
| 2239 | return self.fail("TODO: enable larger branch offset", .{}); | 2341 | .data = .{ .inst = inst }, |
| 2240 | } | 2342 | }); |
| 2241 | } | 2343 | } |
| 2242 | | 2344 | |
| 2243 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { | 2345 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2273,28 +2375,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2273,28 +2375,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2273 | // return self.finishAir(inst, .dead, .{ condition, .none, .none }); | 2375 | // return self.finishAir(inst, .dead, .{ condition, .none, .none }); |
| 2274 | } | 2376 | } |
| 2275 | | 2377 | |
| 2276 | fn performReloc(self: *Self, reloc: Reloc) !void { | 2378 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |
| 2277 | switch (reloc) { | 2379 | const tag = self.mir_instructions.items(.tag)[inst]; |
| 2278 | .rel32 => |pos| { | 2380 | switch (tag) { |
| 2279 | const amt = self.code.items.len - (pos + 4); | 2381 | .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Air.Inst.Index, self.mir_instructions.len), |
| 2280 | // Here it would be tempting to implement testing for amt == 0 and then elide the | 2382 | else => unreachable, |
| 2281 | // jump. However, that will cause a problem because other jumps may assume that they | | |
| 2282 | // can jump to this code. Or maybe I didn't understand something when I was debugging. | | |
| 2283 | // It could be worth another look. Anyway, that's why that isn't done here. Probably the | | |
| 2284 | // best place to elide jumps will be in semantic analysis, by inlining blocks that only | | |
| 2285 | // only have 1 break instruction. | | |
| 2286 | const s32_amt = math.cast(i32, amt) catch | | |
| 2287 | return self.fail("unable to perform relocation: jump too far", .{}); | | |
| 2288 | mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt); | | |
| 2289 | }, | | |
| 2290 | .arm_branch => |info| { | | |
| 2291 | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, info.pos + 8); | | |
| 2292 | if (math.cast(i26, amt)) |delta| { | | |
| 2293 | self.writeInt(u32, self.code.items[info.pos..][0..4], Instruction.b(info.cond, delta).toU32()); | | |
| 2294 | } else |_| { | | |
| 2295 | return self.fail("TODO: enable larger branch offset", .{}); | | |
| 2296 | } | | |
| 2297 | }, | | |
| 2298 | } | 2383 | } |
| 2299 | } | 2384 | } |
| 2300 | | 2385 | |
| ... | @@ -2334,15 +2419,11 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { | ... | @@ -2334,15 +2419,11 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2334 | const block_data = self.blocks.getPtr(block).?; | 2419 | const block_data = self.blocks.getPtr(block).?; |
| 2335 | | 2420 | |
| 2336 | // Emit a jump with a relocation. It will be patched up after the block ends. | 2421 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 2337 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); | 2422 | try block_data.relocs.append(self.gpa, try self.addInst(.{ |
| 2338 | | 2423 | .tag = .b, |
| 2339 | try self.code.resize(self.code.items.len + 4); | 2424 | .cond = .al, |
| 2340 | block_data.relocs.appendAssumeCapacity(.{ | 2425 | .data = .{ .inst = undefined }, // populated later through performReloc |
| 2341 | .arm_branch = .{ | 2426 | })); |
| 2342 | .pos = self.code.items.len - 4, | | |
| 2343 | .cond = .al, | | |
| 2344 | }, | | |
| 2345 | }); | | |
| 2346 | } | 2427 | } |
| 2347 | | 2428 | |
| 2348 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | 2429 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2394,7 +2475,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2394,7 +2475,11 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 2394 | } | 2475 | } |
| 2395 | | 2476 | |
| 2396 | if (mem.eql(u8, asm_source, "svc #0")) { | 2477 | if (mem.eql(u8, asm_source, "svc #0")) { |
| 2397 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.svc(.al, 0).toU32()); | 2478 | _ = try self.addInst(.{ |
| | 2479 | .tag = .svc, |
| | 2480 | .cond = .al, |
| | 2481 | .data = .{ .imm24 = 0 }, |
| | 2482 | }); |
| 2398 | } else { | 2483 | } else { |
| 2399 | return self.fail("TODO implement support for more arm assembly instructions", .{}); | 2484 | return self.fail("TODO implement support for more arm assembly instructions", .{}); |
| 2400 | } | 2485 | } |
| ... | @@ -2490,26 +2575,43 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -2490,26 +2575,43 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2490 | const offset = if (math.cast(u12, adj_off)) |imm| blk: { | 2575 | const offset = if (math.cast(u12, adj_off)) |imm| blk: { |
| 2491 | break :blk Instruction.Offset.imm(imm); | 2576 | break :blk Instruction.Offset.imm(imm); |
| 2492 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); | 2577 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); |
| 2493 | const str = switch (abi_size) { | 2578 | |
| 2494 | 1 => Instruction.strb, | 2579 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2495 | 4 => Instruction.str, | 2580 | 1 => .strb, |
| | 2581 | 4 => .str, |
| 2496 | else => unreachable, | 2582 | else => unreachable, |
| 2497 | }; | 2583 | }; |
| 2498 | | 2584 | |
| 2499 | self.writeInt(u32, try self.code.addManyAsArray(4), str(.al, reg, .fp, .{ | 2585 | _ = try self.addInst(.{ |
| 2500 | .offset = offset, | 2586 | .tag = tag, |
| 2501 | .positive = false, | 2587 | .cond = .al, |
| 2502 | }).toU32()); | 2588 | .data = .{ .rr_offset = .{ |
| | 2589 | .rt = reg, |
| | 2590 | .rn = .fp, |
| | 2591 | .offset = .{ |
| | 2592 | .offset = offset, |
| | 2593 | .positive = false, |
| | 2594 | }, |
| | 2595 | } }, |
| | 2596 | }); |
| 2503 | }, | 2597 | }, |
| 2504 | 2 => { | 2598 | 2 => { |
| 2505 | const offset = if (adj_off <= math.maxInt(u8)) blk: { | 2599 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2506 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); | 2600 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2507 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); | 2601 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); |
| 2508 | | 2602 | |
| 2509 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.strh(.al, reg, .fp, .{ | 2603 | _ = try self.addInst(.{ |
| 2510 | .offset = offset, | 2604 | .tag = .strh, |
| 2511 | .positive = false, | 2605 | .cond = .al, |
| 2512 | }).toU32()); | 2606 | .data = .{ .rr_extra_offset = .{ |
| | 2607 | .rt = reg, |
| | 2608 | .rn = .fp, |
| | 2609 | .offset = .{ |
| | 2610 | .offset = offset, |
| | 2611 | .positive = false, |
| | 2612 | }, |
| | 2613 | } }, |
| | 2614 | }); |
| 2513 | }, | 2615 | }, |
| 2514 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 2616 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 2515 | } | 2617 | } |
| ... | @@ -2549,26 +2651,83 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2549,26 +2651,83 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2549 | else => unreachable, | 2651 | else => unreachable, |
| 2550 | }; | 2652 | }; |
| 2551 | | 2653 | |
| 2552 | // mov reg, 0 | | |
| 2553 | // moveq reg, 1 | | |
| 2554 | const zero = Instruction.Operand.imm(0, 0); | 2654 | const zero = Instruction.Operand.imm(0, 0); |
| 2555 | const one = Instruction.Operand.imm(1, 0); | 2655 | const one = Instruction.Operand.imm(1, 0); |
| 2556 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32()); | 2656 | |
| 2557 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32()); | 2657 | // mov reg, 0 |
| | 2658 | _ = try self.addInst(.{ |
| | 2659 | .tag = .mov, |
| | 2660 | .cond = .al, |
| | 2661 | .data = .{ .rr_op = .{ |
| | 2662 | .rd = reg, |
| | 2663 | .rn = .r0, |
| | 2664 | .op = zero, |
| | 2665 | } }, |
| | 2666 | }); |
| | 2667 | |
| | 2668 | // moveq reg, 1 |
| | 2669 | _ = try self.addInst(.{ |
| | 2670 | .tag = .mov, |
| | 2671 | .cond = condition, |
| | 2672 | .data = .{ .rr_op = .{ |
| | 2673 | .rd = reg, |
| | 2674 | .rn = .r0, |
| | 2675 | .op = one, |
| | 2676 | } }, |
| | 2677 | }); |
| 2558 | }, | 2678 | }, |
| 2559 | .immediate => |x| { | 2679 | .immediate => |x| { |
| 2560 | if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{}); | 2680 | if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{}); |
| 2561 | | 2681 | |
| 2562 | if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| { | 2682 | if (Instruction.Operand.fromU32(@intCast(u32, x))) |op| { |
| 2563 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, op).toU32()); | 2683 | _ = try self.addInst(.{ |
| | 2684 | .tag = .mov, |
| | 2685 | .cond = .al, |
| | 2686 | .data = .{ .rr_op = .{ |
| | 2687 | .rd = reg, |
| | 2688 | .rn = .r0, |
| | 2689 | .op = op, |
| | 2690 | } }, |
| | 2691 | }); |
| 2564 | } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| { | 2692 | } else if (Instruction.Operand.fromU32(~@intCast(u32, x))) |op| { |
| 2565 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mvn(.al, reg, op).toU32()); | 2693 | _ = try self.addInst(.{ |
| | 2694 | .tag = .mvn, |
| | 2695 | .cond = .al, |
| | 2696 | .data = .{ .rr_op = .{ |
| | 2697 | .rd = reg, |
| | 2698 | .rn = .r0, |
| | 2699 | .op = op, |
| | 2700 | } }, |
| | 2701 | }); |
| 2566 | } else if (x <= math.maxInt(u16)) { | 2702 | } else if (x <= math.maxInt(u16)) { |
| 2567 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) { | 2703 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v7)) { |
| 2568 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @intCast(u16, x)).toU32()); | 2704 | _ = try self.addInst(.{ |
| | 2705 | .tag = .movw, |
| | 2706 | .cond = .al, |
| | 2707 | .data = .{ .r_imm16 = .{ |
| | 2708 | .rd = reg, |
| | 2709 | .imm16 = @intCast(u16, x), |
| | 2710 | } }, |
| | 2711 | }); |
| 2569 | } else { | 2712 | } else { |
| 2570 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | 2713 | _ = try self.addInst(.{ |
| 2571 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | 2714 | .tag = .mov, |
| | 2715 | .cond = .al, |
| | 2716 | .data = .{ .rr_op = .{ |
| | 2717 | .rd = reg, |
| | 2718 | .rn = .r0, |
| | 2719 | .op = Instruction.Operand.imm(@truncate(u8, x), 0), |
| | 2720 | } }, |
| | 2721 | }); |
| | 2722 | _ = try self.addInst(.{ |
| | 2723 | .tag = .orr, |
| | 2724 | .cond = .al, |
| | 2725 | .data = .{ .rr_op = .{ |
| | 2726 | .rd = reg, |
| | 2727 | .rn = reg, |
| | 2728 | .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12), |
| | 2729 | } }, |
| | 2730 | }); |
| 2572 | } | 2731 | } |
| 2573 | } else { | 2732 | } else { |
| 2574 | // TODO write constant to code and load | 2733 | // TODO write constant to code and load |
| ... | @@ -2577,18 +2736,64 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2577,18 +2736,64 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2577 | // immediate: 0xaaaabbbb | 2736 | // immediate: 0xaaaabbbb |
| 2578 | // movw reg, #0xbbbb | 2737 | // movw reg, #0xbbbb |
| 2579 | // movt reg, #0xaaaa | 2738 | // movt reg, #0xaaaa |
| 2580 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @truncate(u16, x)).toU32()); | 2739 | _ = try self.addInst(.{ |
| 2581 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movt(.al, reg, @truncate(u16, x >> 16)).toU32()); | 2740 | .tag = .movw, |
| | 2741 | .cond = .al, |
| | 2742 | .data = .{ .r_imm16 = .{ |
| | 2743 | .rd = reg, |
| | 2744 | .imm16 = @truncate(u16, x), |
| | 2745 | } }, |
| | 2746 | }); |
| | 2747 | _ = try self.addInst(.{ |
| | 2748 | .tag = .movt, |
| | 2749 | .cond = .al, |
| | 2750 | .data = .{ .r_imm16 = .{ |
| | 2751 | .rd = reg, |
| | 2752 | .imm16 = @truncate(u16, x >> 16), |
| | 2753 | } }, |
| | 2754 | }); |
| 2582 | } else { | 2755 | } else { |
| 2583 | // immediate: 0xaabbccdd | 2756 | // immediate: 0xaabbccdd |
| 2584 | // mov reg, #0xaa | 2757 | // mov reg, #0xaa |
| 2585 | // orr reg, reg, #0xbb, 24 | 2758 | // orr reg, reg, #0xbb, 24 |
| 2586 | // orr reg, reg, #0xcc, 16 | 2759 | // orr reg, reg, #0xcc, 16 |
| 2587 | // orr reg, reg, #0xdd, 8 | 2760 | // orr reg, reg, #0xdd, 8 |
| 2588 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); | 2761 | _ = try self.addInst(.{ |
| 2589 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); | 2762 | .tag = .mov, |
| 2590 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); | 2763 | .cond = .al, |
| 2591 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); | 2764 | .data = .{ .rr_op = .{ |
| | 2765 | .rd = reg, |
| | 2766 | .rn = .r0, |
| | 2767 | .op = Instruction.Operand.imm(@truncate(u8, x), 0), |
| | 2768 | } }, |
| | 2769 | }); |
| | 2770 | _ = try self.addInst(.{ |
| | 2771 | .tag = .orr, |
| | 2772 | .cond = .al, |
| | 2773 | .data = .{ .rr_op = .{ |
| | 2774 | .rd = reg, |
| | 2775 | .rn = reg, |
| | 2776 | .op = Instruction.Operand.imm(@truncate(u8, x >> 8), 12), |
| | 2777 | } }, |
| | 2778 | }); |
| | 2779 | _ = try self.addInst(.{ |
| | 2780 | .tag = .orr, |
| | 2781 | .cond = .al, |
| | 2782 | .data = .{ .rr_op = .{ |
| | 2783 | .rd = reg, |
| | 2784 | .rn = reg, |
| | 2785 | .op = Instruction.Operand.imm(@truncate(u8, x >> 16), 8), |
| | 2786 | } }, |
| | 2787 | }); |
| | 2788 | _ = try self.addInst(.{ |
| | 2789 | .tag = .orr, |
| | 2790 | .cond = .al, |
| | 2791 | .data = .{ .rr_op = .{ |
| | 2792 | .rd = reg, |
| | 2793 | .rn = reg, |
| | 2794 | .op = Instruction.Operand.imm(@truncate(u8, x >> 24), 4), |
| | 2795 | } }, |
| | 2796 | }); |
| 2592 | } | 2797 | } |
| 2593 | } | 2798 | } |
| 2594 | }, | 2799 | }, |
| ... | @@ -2598,13 +2803,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2598,13 +2803,29 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2598 | return; | 2803 | return; |
| 2599 | | 2804 | |
| 2600 | // mov reg, src_reg | 2805 | // mov reg, src_reg |
| 2601 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none)).toU32()); | 2806 | _ = try self.addInst(.{ |
| | 2807 | .tag = .mov, |
| | 2808 | .cond = .al, |
| | 2809 | .data = .{ .rr_op = .{ |
| | 2810 | .rd = reg, |
| | 2811 | .rn = .r0, |
| | 2812 | .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none), |
| | 2813 | } }, |
| | 2814 | }); |
| 2602 | }, | 2815 | }, |
| 2603 | .memory => |addr| { | 2816 | .memory => |addr| { |
| 2604 | // The value is in memory at a hard-coded address. | 2817 | // The value is in memory at a hard-coded address. |
| 2605 | // If the type is a pointer, it means the pointer address is at this memory location. | 2818 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2606 | try self.genSetReg(ty, reg, .{ .immediate = addr }); | 2819 | try self.genSetReg(ty, reg, .{ .immediate = addr }); |
| 2607 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(.al, reg, reg, .{ .offset = Instruction.Offset.none }).toU32()); | 2820 | _ = try self.addInst(.{ |
| | 2821 | .tag = .ldr, |
| | 2822 | .cond = .al, |
| | 2823 | .data = .{ .rr_offset = .{ |
| | 2824 | .rt = reg, |
| | 2825 | .rn = reg, |
| | 2826 | .offset = .{ .offset = Instruction.Offset.none }, |
| | 2827 | } }, |
| | 2828 | }); |
| 2608 | }, | 2829 | }, |
| 2609 | .stack_offset => |unadjusted_off| { | 2830 | .stack_offset => |unadjusted_off| { |
| 2610 | // TODO: maybe addressing from sp instead of fp | 2831 | // TODO: maybe addressing from sp instead of fp |
| ... | @@ -2616,26 +2837,43 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -2616,26 +2837,43 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2616 | const offset = if (adj_off <= math.maxInt(u12)) blk: { | 2837 | const offset = if (adj_off <= math.maxInt(u12)) blk: { |
| 2617 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); | 2838 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); |
| 2618 | } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); | 2839 | } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); |
| 2619 | const ldr = switch (abi_size) { | 2840 | |
| 2620 | 1 => Instruction.ldrb, | 2841 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2621 | 4 => Instruction.ldr, | 2842 | 1 => .ldrb, |
| | 2843 | 4 => .ldr, |
| 2622 | else => unreachable, | 2844 | else => unreachable, |
| 2623 | }; | 2845 | }; |
| 2624 | | 2846 | |
| 2625 | self.writeInt(u32, try self.code.addManyAsArray(4), ldr(.al, reg, .fp, .{ | 2847 | _ = try self.addInst(.{ |
| 2626 | .offset = offset, | 2848 | .tag = tag, |
| 2627 | .positive = false, | 2849 | .cond = .al, |
| 2628 | }).toU32()); | 2850 | .data = .{ .rr_offset = .{ |
| | 2851 | .rt = reg, |
| | 2852 | .rn = .fp, |
| | 2853 | .offset = .{ |
| | 2854 | .offset = offset, |
| | 2855 | .positive = false, |
| | 2856 | }, |
| | 2857 | } }, |
| | 2858 | }); |
| 2629 | }, | 2859 | }, |
| 2630 | 2 => { | 2860 | 2 => { |
| 2631 | const offset = if (adj_off <= math.maxInt(u8)) blk: { | 2861 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2632 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); | 2862 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2633 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); | 2863 | } else Instruction.ExtraLoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off })); |
| 2634 | | 2864 | |
| 2635 | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldrh(.al, reg, .fp, .{ | 2865 | _ = try self.addInst(.{ |
| 2636 | .offset = offset, | 2866 | .tag = .ldrh, |
| 2637 | .positive = false, | 2867 | .cond = .al, |
| 2638 | }).toU32()); | 2868 | .data = .{ .rr_extra_offset = .{ |
| | 2869 | .rt = reg, |
| | 2870 | .rn = .fp, |
| | 2871 | .offset = .{ |
| | 2872 | .offset = offset, |
| | 2873 | .positive = false, |
| | 2874 | }, |
| | 2875 | } }, |
| | 2876 | }); |
| 2639 | }, | 2877 | }, |
| 2640 | else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}), | 2878 | else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}), |
| 2641 | } | 2879 | } |