| ... | ... | @@ -5,6 +5,8 @@ const math = std.math; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | 6 | const Air = @import("../../Air.zig"); |
| 7 | 7 | const Zir = @import("../../Zir.zig"); |
| 8 | const Mir = @import("Mir.zig"); |
| 9 | const Emit = @import("Emit.zig"); |
| 8 | 10 | const Liveness = @import("../../Liveness.zig"); |
| 9 | 11 | const Type = @import("../../type.zig").Type; |
| 10 | 12 | const Value = @import("../../value.zig").Value; |
| ... | ... | @@ -22,9 +24,9 @@ const log = std.log.scoped(.codegen); |
| 22 | 24 | const build_options = @import("build_options"); |
| 23 | 25 | const RegisterManager = @import("../../register_manager.zig").RegisterManager; |
| 24 | 26 | |
| 25 | | pub const FnResult = @import("../../codegen.zig").FnResult; |
| 26 | | pub const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 27 | | pub const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 27 | const FnResult = @import("../../codegen.zig").FnResult; |
| 28 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 29 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 28 | 30 | |
| 29 | 31 | const InnerError = error{ |
| 30 | 32 | OutOfMemory, |
| ... | ... | @@ -37,8 +39,6 @@ liveness: Liveness, |
| 37 | 39 | bin_file: *link.File, |
| 38 | 40 | target: *const std.Target, |
| 39 | 41 | mod_fn: *const Module.Fn, |
| 40 | | code: *std.ArrayList(u8), |
| 41 | | debug_output: DebugInfoOutput, |
| 42 | 42 | err_msg: ?*ErrorMsg, |
| 43 | 43 | args: []MCValue, |
| 44 | 44 | ret_mcv: MCValue, |
| ... | ... | @@ -47,13 +47,14 @@ arg_index: usize, |
| 47 | 47 | src_loc: Module.SrcLoc, |
| 48 | 48 | stack_align: u32, |
| 49 | 49 | |
| 50 | | prev_di_line: u32, |
| 51 | | prev_di_column: u32, |
| 50 | /// MIR Instructions |
| 51 | mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, |
| 52 | /// MIR extra data |
| 53 | mir_extra: std.ArrayListUnmanaged(u32) = .{}, |
| 54 | |
| 52 | 55 | /// Byte offset within the source file of the ending curly. |
| 53 | 56 | end_di_line: u32, |
| 54 | 57 | end_di_column: u32, |
| 55 | | /// Relative to the beginning of `code`. |
| 56 | | prev_di_pc: usize, |
| 57 | 58 | |
| 58 | 59 | /// The value is an offset into the `Function` `code` from the beginning. |
| 59 | 60 | /// To perform the reloc, write 32-bit signed little-endian integer |
| ... | ... | @@ -176,7 +177,7 @@ const StackAllocation = struct { |
| 176 | 177 | }; |
| 177 | 178 | |
| 178 | 179 | const BlockData = struct { |
| 179 | | relocs: std.ArrayListUnmanaged(Reloc), |
| 180 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index), |
| 180 | 181 | /// The first break instruction encounters `null` here and chooses a |
| 181 | 182 | /// machine code value for the block result, populating this field. |
| 182 | 183 | /// Following break instructions encounter that value and use it for |
| ... | ... | @@ -184,18 +185,6 @@ const BlockData = struct { |
| 184 | 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 | 188 | const BigTomb = struct { |
| 200 | 189 | function: *Self, |
| 201 | 190 | inst: Air.Inst.Index, |
| ... | ... | @@ -266,8 +255,6 @@ pub fn generate( |
| 266 | 255 | .target = &bin_file.options.target, |
| 267 | 256 | .bin_file = bin_file, |
| 268 | 257 | .mod_fn = module_fn, |
| 269 | | .code = code, |
| 270 | | .debug_output = debug_output, |
| 271 | 258 | .err_msg = null, |
| 272 | 259 | .args = undefined, // populated after `resolveCallingConventionValues` |
| 273 | 260 | .ret_mcv = undefined, // populated after `resolveCallingConventionValues` |
| ... | ... | @@ -276,9 +263,6 @@ pub fn generate( |
| 276 | 263 | .branch_stack = &branch_stack, |
| 277 | 264 | .src_loc = src_loc, |
| 278 | 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 | 266 | .end_di_line = module_fn.rbrace_line, |
| 283 | 267 | .end_di_column = module_fn.rbrace_column, |
| 284 | 268 | }; |
| ... | ... | @@ -302,6 +286,30 @@ pub fn generate( |
| 302 | 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 | 313 | if (function.err_msg) |em| { |
| 306 | 314 | return FnResult{ .fail = em }; |
| 307 | 315 | } else { |
| ... | ... | @@ -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 | 357 | fn gen(self: *Self) !void { |
| 313 | 358 | const cc = self.fn_type.fnCallingConvention(); |
| 314 | 359 | if (cc != .Naked) { |
| 315 | 360 | // push {fp, lr} |
| 361 | const push_reloc = try self.addNop(); |
| 362 | |
| 316 | 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 | 374 | // sub sp, sp, #reloc |
| 318 | | const prologue_reloc = self.code.items.len; |
| 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()); |
| 375 | const sub_reloc = try self.addNop(); |
| 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 | 383 | try self.genBody(self.air.getMainBody()); |
| 325 | 384 | |
| ... | ... | @@ -333,18 +392,30 @@ fn gen(self: *Self) !void { |
| 333 | 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 | 401 | // Backpatch stack offset |
| 339 | 402 | const stack_end = self.max_end_stack; |
| 340 | 403 | const aligned_stack_end = mem.alignForward(stack_end, self.stack_align); |
| 341 | 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 | 410 | } else { |
| 344 | 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 | 420 | // exitlude jumps |
| 350 | 421 | if (self.exitlude_jump_relocs.items.len == 1) { |
| ... | ... | @@ -353,23 +424,13 @@ fn gen(self: *Self) !void { |
| 353 | 424 | // the code. Therefore, we can just delete |
| 354 | 425 | // the space initially reserved for the |
| 355 | 426 | // jump |
| 356 | | self.code.items.len -= 4; |
| 427 | self.mir_instructions.len -= 1; |
| 357 | 428 | } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 358 | | const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8); |
| 359 | | if (amt == -4) { |
| 360 | | // This return is at the end of the |
| 361 | | // code block. We can't just delete |
| 362 | | // the space because there may be |
| 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 | | } |
| 429 | self.mir_instructions.set(jmp_reloc, .{ |
| 430 | .tag = .b, |
| 431 | .cond = .al, |
| 432 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, |
| 433 | }); |
| 373 | 434 | } |
| 374 | 435 | |
| 375 | 436 | // Epilogue: pop callee saved registers (swap lr with pc in saved_regs) |
| ... | ... | @@ -377,17 +438,47 @@ fn gen(self: *Self) !void { |
| 377 | 438 | saved_regs.r15 = true; // pc |
| 378 | 439 | |
| 379 | 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 | 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()); |
| 382 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldm(.al, .sp, true, saved_regs).toU32()); |
| 452 | _ = try self.addInst(.{ |
| 453 | .tag = .pop, |
| 454 | .cond = .al, |
| 455 | .data = .{ .register_list = saved_regs }, |
| 456 | }); |
| 383 | 457 | } else { |
| 384 | | try self.dbgSetPrologueEnd(); |
| 458 | _ = try self.addInst(.{ |
| 459 | .tag = .dbg_prologue_end, |
| 460 | .cond = undefined, |
| 461 | .data = .{ .nop = {} }, |
| 462 | }); |
| 463 | |
| 385 | 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 | 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 | 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 | 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 | 628 | /// Asserts there is already capacity to insert into top branch inst_table. |
| 611 | 629 | fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 612 | 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 | 1235 | .compare_flags_signed, .compare_flags_unsigned => unreachable, |
| 1218 | 1236 | .embedded_in_code => unreachable, |
| 1219 | 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 | 1248 | else => return self.fail("TODO load from register into {}", .{dst_mcv}), |
| 1223 | 1249 | } |
| ... | ... | @@ -1513,48 +1539,81 @@ fn genArmBinOpCode( |
| 1513 | 1539 | }; |
| 1514 | 1540 | |
| 1515 | 1541 | switch (op) { |
| 1516 | | .add => { |
| 1517 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32()); |
| 1542 | .add, |
| 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 | 1568 | .sub => { |
| 1520 | | if (swap_lhs_and_rhs) { |
| 1521 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32()); |
| 1522 | | } else { |
| 1523 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32()); |
| 1524 | | } |
| 1525 | | }, |
| 1526 | | .bool_and, .bit_and => { |
| 1527 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32()); |
| 1528 | | }, |
| 1529 | | .bool_or, .bit_or => { |
| 1530 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32()); |
| 1531 | | }, |
| 1532 | | .not, .xor => { |
| 1533 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32()); |
| 1569 | const tag: Mir.Inst.Tag = if (swap_lhs_and_rhs) .rsb else .sub; |
| 1570 | |
| 1571 | _ = try self.addInst(.{ |
| 1572 | .tag = tag, |
| 1573 | .cond = .al, |
| 1574 | .data = .{ .rr_op = .{ |
| 1575 | .rd = dst_reg, |
| 1576 | .rn = op1, |
| 1577 | .op = operand, |
| 1578 | } }, |
| 1579 | }); |
| 1534 | 1580 | }, |
| 1535 | 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 => { |
| 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 => { |
| 1592 | .shl, .shr => { |
| 1547 | 1593 | assert(!swap_lhs_and_rhs); |
| 1548 | 1594 | const shift_amount = switch (operand) { |
| 1549 | 1595 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1550 | 1596 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1551 | 1597 | }; |
| 1552 | 1598 | |
| 1553 | | const shr = switch (signedness) { |
| 1554 | | .signed => Instruction.asr, |
| 1555 | | .unsigned => Instruction.lsr, |
| 1599 | const tag: Mir.Inst.Tag = switch (op) { |
| 1600 | .shl => .lsl, |
| 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 | 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 | 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 | 1694 | return dst_mcv; |
| 1628 | 1695 | } |
| 1629 | 1696 | |
| ... | ... | @@ -1707,7 +1774,8 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1707 | 1774 | }, |
| 1708 | 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 | 1780 | if (self.liveness.isUnused(inst)) |
| 1713 | 1781 | return self.finishAirBookkeeping(); |
| ... | ... | @@ -1723,7 +1791,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 1723 | 1791 | } |
| 1724 | 1792 | |
| 1725 | 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 | 1799 | return self.finishAirBookkeeping(); |
| 1728 | 1800 | } |
| 1729 | 1801 | |
| ... | ... | @@ -1794,10 +1866,27 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 1794 | 1866 | // TODO: add Instruction.supportedOn |
| 1795 | 1867 | // function for ARM |
| 1796 | 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 | 1874 | } else { |
| 1799 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, .lr, Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none)).toU32()); |
| 1800 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.bx(.al, .lr).toU32()); |
| 1875 | return self.fail("TODO fix blx emulatio for ARM <v5", .{}); |
| 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 | 1891 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 1803 | 1892 | return self.fail("TODO implement calling extern functions", .{}); |
| ... | ... | @@ -1845,8 +1934,7 @@ fn ret(self: *Self, mcv: MCValue) !void { |
| 1845 | 1934 | try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); |
| 1846 | 1935 | |
| 1847 | 1936 | // Just add space for an instruction, patch this later |
| 1848 | | try self.code.resize(self.code.items.len + 4); |
| 1849 | | try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4); |
| 1937 | try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); |
| 1850 | 1938 | } |
| 1851 | 1939 | |
| 1852 | 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 | 2013 | |
| 1926 | 2014 | fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1927 | 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 | 2026 | return self.finishAirBookkeeping(); |
| 1930 | 2027 | } |
| 1931 | 2028 | |
| ... | ... | @@ -1937,7 +2034,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1937 | 2034 | const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 1938 | 2035 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 1939 | 2036 | |
| 1940 | | const reloc: Reloc = reloc: { |
| 2037 | const reloc: Mir.Inst.Index = reloc: { |
| 1941 | 2038 | const condition: Condition = switch (cond) { |
| 1942 | 2039 | .compare_flags_signed => |cmp_op| blk: { |
| 1943 | 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 | 2049 | .register => |reg| blk: { |
| 1953 | 2050 | // cmp reg, 1 |
| 1954 | 2051 | // bne ... |
| 1955 | | const op = Instruction.Operand.imm(1, 0); |
| 1956 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, reg, op).toU32()); |
| 2052 | _ = try self.addInst(.{ |
| 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 | 2062 | break :blk .ne; |
| 1958 | 2063 | }, |
| 1959 | 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{ |
| 1963 | | .arm_branch = .{ |
| 1964 | | .pos = self.code.items.len, |
| 1965 | | .cond = condition, |
| 1966 | | }, |
| 1967 | | }; |
| 1968 | | try self.code.resize(self.code.items.len + 4); |
| 1969 | | break :reloc reloc; |
| 2067 | break :reloc try self.addInst(.{ |
| 2068 | .tag = .b, |
| 2069 | .cond = condition, |
| 2070 | .data = .{ .inst = undefined }, // populated later through performReloc |
| 2071 | }); |
| 1970 | 2072 | }; |
| 1971 | 2073 | |
| 1972 | 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 | 2327 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2226 | 2328 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2227 | 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 | 2331 | try self.genBody(body); |
| 2230 | 2332 | try self.jump(start_index); |
| 2231 | 2333 | return self.finishAirBookkeeping(); |
| 2232 | 2334 | } |
| 2233 | 2335 | |
| 2234 | | /// Send control flow to the `index` of `self.code`. |
| 2235 | | fn jump(self: *Self, index: usize) !void { |
| 2236 | | if (math.cast(i26, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| { |
| 2237 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(.al, delta).toU32()); |
| 2238 | | } else |_| { |
| 2239 | | return self.fail("TODO: enable larger branch offset", .{}); |
| 2240 | | } |
| 2336 | /// Send control flow to `inst`. |
| 2337 | fn jump(self: *Self, inst: Mir.Inst.Index) !void { |
| 2338 | _ = try self.addInst(.{ |
| 2339 | .tag = .b, |
| 2340 | .cond = .al, |
| 2341 | .data = .{ .inst = inst }, |
| 2342 | }); |
| 2241 | 2343 | } |
| 2242 | 2344 | |
| 2243 | 2345 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2273,28 +2375,11 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2273 | 2375 | // return self.finishAir(inst, .dead, .{ condition, .none, .none }); |
| 2274 | 2376 | } |
| 2275 | 2377 | |
| 2276 | | fn performReloc(self: *Self, reloc: Reloc) !void { |
| 2277 | | switch (reloc) { |
| 2278 | | .rel32 => |pos| { |
| 2279 | | const amt = self.code.items.len - (pos + 4); |
| 2280 | | // Here it would be tempting to implement testing for amt == 0 and then elide the |
| 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 | | }, |
| 2378 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |
| 2379 | const tag = self.mir_instructions.items(.tag)[inst]; |
| 2380 | switch (tag) { |
| 2381 | .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Air.Inst.Index, self.mir_instructions.len), |
| 2382 | else => unreachable, |
| 2298 | 2383 | } |
| 2299 | 2384 | } |
| 2300 | 2385 | |
| ... | ... | @@ -2334,15 +2419,11 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2334 | 2419 | const block_data = self.blocks.getPtr(block).?; |
| 2335 | 2420 | |
| 2336 | 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); |
| 2338 | | |
| 2339 | | try self.code.resize(self.code.items.len + 4); |
| 2340 | | block_data.relocs.appendAssumeCapacity(.{ |
| 2341 | | .arm_branch = .{ |
| 2342 | | .pos = self.code.items.len - 4, |
| 2343 | | .cond = .al, |
| 2344 | | }, |
| 2345 | | }); |
| 2422 | try block_data.relocs.append(self.gpa, try self.addInst(.{ |
| 2423 | .tag = .b, |
| 2424 | .cond = .al, |
| 2425 | .data = .{ .inst = undefined }, // populated later through performReloc |
| 2426 | })); |
| 2346 | 2427 | } |
| 2347 | 2428 | |
| 2348 | 2429 | 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 | 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 | 2483 | } else { |
| 2399 | 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 | 2575 | const offset = if (math.cast(u12, adj_off)) |imm| blk: { |
| 2491 | 2576 | break :blk Instruction.Offset.imm(imm); |
| 2492 | 2577 | } else |_| Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); |
| 2493 | | const str = switch (abi_size) { |
| 2494 | | 1 => Instruction.strb, |
| 2495 | | 4 => Instruction.str, |
| 2578 | |
| 2579 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2580 | 1 => .strb, |
| 2581 | 4 => .str, |
| 2496 | 2582 | else => unreachable, |
| 2497 | 2583 | }; |
| 2498 | 2584 | |
| 2499 | | self.writeInt(u32, try self.code.addManyAsArray(4), str(.al, reg, .fp, .{ |
| 2500 | | .offset = offset, |
| 2501 | | .positive = false, |
| 2502 | | }).toU32()); |
| 2585 | _ = try self.addInst(.{ |
| 2586 | .tag = tag, |
| 2587 | .cond = .al, |
| 2588 | .data = .{ .rr_offset = .{ |
| 2589 | .rt = reg, |
| 2590 | .rn = .fp, |
| 2591 | .offset = .{ |
| 2592 | .offset = offset, |
| 2593 | .positive = false, |
| 2594 | }, |
| 2595 | } }, |
| 2596 | }); |
| 2503 | 2597 | }, |
| 2504 | 2598 | 2 => { |
| 2505 | 2599 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2506 | 2600 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2507 | 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, .{ |
| 2510 | | .offset = offset, |
| 2511 | | .positive = false, |
| 2512 | | }).toU32()); |
| 2603 | _ = try self.addInst(.{ |
| 2604 | .tag = .strh, |
| 2605 | .cond = .al, |
| 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 | 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 | 2651 | else => unreachable, |
| 2550 | 2652 | }; |
| 2551 | 2653 | |
| 2552 | | // mov reg, 0 |
| 2553 | | // moveq reg, 1 |
| 2554 | 2654 | const zero = Instruction.Operand.imm(0, 0); |
| 2555 | 2655 | const one = Instruction.Operand.imm(1, 0); |
| 2556 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, zero).toU32()); |
| 2557 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(condition, reg, one).toU32()); |
| 2656 | |
| 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 | 2679 | .immediate => |x| { |
| 2560 | 2680 | if (x > math.maxInt(u32)) return self.fail("ARM registers are 32-bit wide", .{}); |
| 2561 | 2681 | |
| 2562 | 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 | 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 | 2702 | } else if (x <= math.maxInt(u16)) { |
| 2567 | 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 | 2712 | } else { |
| 2570 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.mov(.al, reg, Instruction.Operand.imm(@truncate(u8, x), 0)).toU32()); |
| 2571 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); |
| 2713 | _ = try self.addInst(.{ |
| 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 | 2732 | } else { |
| 2574 | 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 | 2736 | // immediate: 0xaaaabbbb |
| 2578 | 2737 | // movw reg, #0xbbbb |
| 2579 | 2738 | // movt reg, #0xaaaa |
| 2580 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movw(.al, reg, @truncate(u16, x)).toU32()); |
| 2581 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.movt(.al, reg, @truncate(u16, x >> 16)).toU32()); |
| 2739 | _ = try self.addInst(.{ |
| 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 | 2755 | } else { |
| 2583 | 2756 | // immediate: 0xaabbccdd |
| 2584 | 2757 | // mov reg, #0xaa |
| 2585 | 2758 | // orr reg, reg, #0xbb, 24 |
| 2586 | 2759 | // orr reg, reg, #0xcc, 16 |
| 2587 | 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()); |
| 2589 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 8), 12)).toU32()); |
| 2590 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 16), 8)).toU32()); |
| 2591 | | self.writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, reg, reg, Instruction.Operand.imm(@truncate(u8, x >> 24), 4)).toU32()); |
| 2761 | _ = try self.addInst(.{ |
| 2762 | .tag = .mov, |
| 2763 | .cond = .al, |
| 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 | 2803 | return; |
| 2599 | 2804 | |
| 2600 | 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 | 2816 | .memory => |addr| { |
| 2604 | 2817 | // The value is in memory at a hard-coded address. |
| 2605 | 2818 | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2606 | 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 | 2830 | .stack_offset => |unadjusted_off| { |
| 2610 | 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 | 2837 | const offset = if (adj_off <= math.maxInt(u12)) blk: { |
| 2617 | 2838 | break :blk Instruction.Offset.imm(@intCast(u12, adj_off)); |
| 2618 | 2839 | } else Instruction.Offset.reg(try self.copyToTmpRegister(Type.initTag(.u32), MCValue{ .immediate = adj_off }), 0); |
| 2619 | | const ldr = switch (abi_size) { |
| 2620 | | 1 => Instruction.ldrb, |
| 2621 | | 4 => Instruction.ldr, |
| 2840 | |
| 2841 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2842 | 1 => .ldrb, |
| 2843 | 4 => .ldr, |
| 2622 | 2844 | else => unreachable, |
| 2623 | 2845 | }; |
| 2624 | 2846 | |
| 2625 | | self.writeInt(u32, try self.code.addManyAsArray(4), ldr(.al, reg, .fp, .{ |
| 2626 | | .offset = offset, |
| 2627 | | .positive = false, |
| 2628 | | }).toU32()); |
| 2847 | _ = try self.addInst(.{ |
| 2848 | .tag = tag, |
| 2849 | .cond = .al, |
| 2850 | .data = .{ .rr_offset = .{ |
| 2851 | .rt = reg, |
| 2852 | .rn = .fp, |
| 2853 | .offset = .{ |
| 2854 | .offset = offset, |
| 2855 | .positive = false, |
| 2856 | }, |
| 2857 | } }, |
| 2858 | }); |
| 2629 | 2859 | }, |
| 2630 | 2860 | 2 => { |
| 2631 | 2861 | const offset = if (adj_off <= math.maxInt(u8)) blk: { |
| 2632 | 2862 | break :blk Instruction.ExtraLoadStoreOffset.imm(@intCast(u8, adj_off)); |
| 2633 | 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, .{ |
| 2636 | | .offset = offset, |
| 2637 | | .positive = false, |
| 2638 | | }).toU32()); |
| 2865 | _ = try self.addInst(.{ |
| 2866 | .tag = .ldrh, |
| 2867 | .cond = .al, |
| 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 | 2878 | else => return self.fail("TODO a type of size {} is not allowed in a register", .{abi_size}), |
| 2641 | 2879 | } |