| ... | ... | @@ -22,6 +22,8 @@ const DW = std.dwarf; |
| 22 | 22 | const leb128 = std.leb; |
| 23 | 23 | const log = std.log.scoped(.riscv_codegen); |
| 24 | 24 | const tracking_log = std.log.scoped(.tracking); |
| 25 | const verbose_tracking_log = std.log.scoped(.verbose_tracking); |
| 26 | const wip_mir_log = std.log.scoped(.wip_mir); |
| 25 | 27 | const build_options = @import("build_options"); |
| 26 | 28 | const codegen = @import("../../codegen.zig"); |
| 27 | 29 | const Alignment = InternPool.Alignment; |
| ... | ... | @@ -32,6 +34,8 @@ const DebugInfoOutput = codegen.DebugInfoOutput; |
| 32 | 34 | |
| 33 | 35 | const bits = @import("bits.zig"); |
| 34 | 36 | const abi = @import("abi.zig"); |
| 37 | const Lower = @import("Lower.zig"); |
| 38 | |
| 35 | 39 | const Register = bits.Register; |
| 36 | 40 | const Immediate = bits.Immediate; |
| 37 | 41 | const Memory = bits.Memory; |
| ... | ... | @@ -158,6 +162,14 @@ const MCValue = union(enum) { |
| 158 | 162 | }; |
| 159 | 163 | } |
| 160 | 164 | |
| 165 | fn isRegister(mcv: MCValue) bool { |
| 166 | return switch (mcv) { |
| 167 | .register => true, |
| 168 | .register_offset => |reg_off| return reg_off.off == 0, |
| 169 | else => false, |
| 170 | }; |
| 171 | } |
| 172 | |
| 161 | 173 | fn isMutable(mcv: MCValue) bool { |
| 162 | 174 | return switch (mcv) { |
| 163 | 175 | .none => unreachable, |
| ... | ... | @@ -289,6 +301,7 @@ const Branch = struct { |
| 289 | 301 | |
| 290 | 302 | const InstTrackingMap = std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InstTracking); |
| 291 | 303 | const ConstTrackingMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, InstTracking); |
| 304 | |
| 292 | 305 | const InstTracking = struct { |
| 293 | 306 | long: MCValue, |
| 294 | 307 | short: MCValue, |
| ... | ... | @@ -317,33 +330,37 @@ const InstTracking = struct { |
| 317 | 330 | }, .short = result }; |
| 318 | 331 | } |
| 319 | 332 | |
| 320 | | fn getReg(func: InstTracking) ?Register { |
| 321 | | return func.short.getReg(); |
| 333 | fn getReg(inst_tracking: InstTracking) ?Register { |
| 334 | return inst_tracking.short.getReg(); |
| 322 | 335 | } |
| 323 | 336 | |
| 324 | | fn getRegs(func: *const InstTracking) []const Register { |
| 325 | | return func.short.getRegs(); |
| 337 | fn getRegs(inst_tracking: *const InstTracking) []const Register { |
| 338 | return inst_tracking.short.getRegs(); |
| 326 | 339 | } |
| 327 | 340 | |
| 328 | | fn spill(func: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 329 | | if (std.meta.eql(func.long, func.short)) return; // Already spilled |
| 341 | fn spill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 342 | if (std.meta.eql(inst_tracking.long, inst_tracking.short)) return; // Already spilled |
| 330 | 343 | // Allocate or reuse frame index |
| 331 | | switch (func.long) { |
| 332 | | .none => func.long = try function.allocRegOrMem(inst, false), |
| 344 | switch (inst_tracking.long) { |
| 345 | .none => inst_tracking.long = try function.allocRegOrMem( |
| 346 | function.typeOfIndex(inst), |
| 347 | inst, |
| 348 | false, |
| 349 | ), |
| 333 | 350 | .load_frame => {}, |
| 334 | | .reserved_frame => |index| func.long = .{ .load_frame = .{ .index = index } }, |
| 351 | .reserved_frame => |index| inst_tracking.long = .{ .load_frame = .{ .index = index } }, |
| 335 | 352 | else => unreachable, |
| 336 | 353 | } |
| 337 | | tracking_log.debug("spill %{d} from {} to {}", .{ inst, func.short, func.long }); |
| 338 | | try function.genCopy(function.typeOfIndex(inst), func.long, func.short); |
| 354 | tracking_log.debug("spill %{d} from {} to {}", .{ inst, inst_tracking.short, inst_tracking.long }); |
| 355 | try function.genCopy(function.typeOfIndex(inst), inst_tracking.long, inst_tracking.short); |
| 339 | 356 | } |
| 340 | 357 | |
| 341 | | fn reuseFrame(func: *InstTracking) void { |
| 342 | | switch (func.long) { |
| 343 | | .reserved_frame => |index| func.long = .{ .load_frame = .{ .index = index } }, |
| 358 | fn reuseFrame(inst_tracking: *InstTracking) void { |
| 359 | switch (inst_tracking.long) { |
| 360 | .reserved_frame => |index| inst_tracking.long = .{ .load_frame = .{ .index = index } }, |
| 344 | 361 | else => {}, |
| 345 | 362 | } |
| 346 | | func.short = switch (func.long) { |
| 363 | inst_tracking.short = switch (inst_tracking.long) { |
| 347 | 364 | .none, |
| 348 | 365 | .unreach, |
| 349 | 366 | .undef, |
| ... | ... | @@ -353,7 +370,7 @@ const InstTracking = struct { |
| 353 | 370 | .lea_frame, |
| 354 | 371 | .load_symbol, |
| 355 | 372 | .lea_symbol, |
| 356 | | => func.long, |
| 373 | => inst_tracking.long, |
| 357 | 374 | .dead, |
| 358 | 375 | .register, |
| 359 | 376 | .register_pair, |
| ... | ... | @@ -365,14 +382,14 @@ const InstTracking = struct { |
| 365 | 382 | }; |
| 366 | 383 | } |
| 367 | 384 | |
| 368 | | fn trackSpill(func: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 369 | | try function.freeValue(func.short); |
| 370 | | func.reuseFrame(); |
| 371 | | tracking_log.debug("%{d} => {} (spilled)", .{ inst, func.* }); |
| 385 | fn trackSpill(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 386 | try function.freeValue(inst_tracking.short); |
| 387 | inst_tracking.reuseFrame(); |
| 388 | tracking_log.debug("%{d} => {} (spilled)", .{ inst, inst_tracking.* }); |
| 372 | 389 | } |
| 373 | 390 | |
| 374 | | fn verifyMaterialize(func: InstTracking, target: InstTracking) void { |
| 375 | | switch (func.long) { |
| 391 | fn verifyMaterialize(inst_tracking: InstTracking, target: InstTracking) void { |
| 392 | switch (inst_tracking.long) { |
| 376 | 393 | .none, |
| 377 | 394 | .unreach, |
| 378 | 395 | .undef, |
| ... | ... | @@ -381,7 +398,7 @@ const InstTracking = struct { |
| 381 | 398 | .lea_frame, |
| 382 | 399 | .load_symbol, |
| 383 | 400 | .lea_symbol, |
| 384 | | => assert(std.meta.eql(func.long, target.long)), |
| 401 | => assert(std.meta.eql(inst_tracking.long, target.long)), |
| 385 | 402 | .load_frame, |
| 386 | 403 | .reserved_frame, |
| 387 | 404 | => switch (target.long) { |
| ... | ... | @@ -402,73 +419,73 @@ const InstTracking = struct { |
| 402 | 419 | } |
| 403 | 420 | |
| 404 | 421 | fn materialize( |
| 405 | | func: *InstTracking, |
| 422 | inst_tracking: *InstTracking, |
| 406 | 423 | function: *Func, |
| 407 | 424 | inst: Air.Inst.Index, |
| 408 | 425 | target: InstTracking, |
| 409 | 426 | ) !void { |
| 410 | | func.verifyMaterialize(target); |
| 411 | | try func.materializeUnsafe(function, inst, target); |
| 427 | inst_tracking.verifyMaterialize(target); |
| 428 | try inst_tracking.materializeUnsafe(function, inst, target); |
| 412 | 429 | } |
| 413 | 430 | |
| 414 | 431 | fn materializeUnsafe( |
| 415 | | func: InstTracking, |
| 432 | inst_tracking: InstTracking, |
| 416 | 433 | function: *Func, |
| 417 | 434 | inst: Air.Inst.Index, |
| 418 | 435 | target: InstTracking, |
| 419 | 436 | ) !void { |
| 420 | 437 | const ty = function.typeOfIndex(inst); |
| 421 | | if ((func.long == .none or func.long == .reserved_frame) and target.long == .load_frame) |
| 422 | | try function.genCopy(ty, target.long, func.short); |
| 423 | | try function.genCopy(ty, target.short, func.short); |
| 438 | if ((inst_tracking.long == .none or inst_tracking.long == .reserved_frame) and target.long == .load_frame) |
| 439 | try function.genCopy(ty, target.long, inst_tracking.short); |
| 440 | try function.genCopy(ty, target.short, inst_tracking.short); |
| 424 | 441 | } |
| 425 | 442 | |
| 426 | | fn trackMaterialize(func: *InstTracking, inst: Air.Inst.Index, target: InstTracking) void { |
| 427 | | func.verifyMaterialize(target); |
| 443 | fn trackMaterialize(inst_tracking: *InstTracking, inst: Air.Inst.Index, target: InstTracking) void { |
| 444 | inst_tracking.verifyMaterialize(target); |
| 428 | 445 | // Don't clobber reserved frame indices |
| 429 | | func.long = if (target.long == .none) switch (func.long) { |
| 446 | inst_tracking.long = if (target.long == .none) switch (inst_tracking.long) { |
| 430 | 447 | .load_frame => |addr| .{ .reserved_frame = addr.index }, |
| 431 | | .reserved_frame => func.long, |
| 448 | .reserved_frame => inst_tracking.long, |
| 432 | 449 | else => target.long, |
| 433 | 450 | } else target.long; |
| 434 | | func.short = target.short; |
| 435 | | tracking_log.debug("%{d} => {} (materialize)", .{ inst, func.* }); |
| 451 | inst_tracking.short = target.short; |
| 452 | tracking_log.debug("%{d} => {} (materialize)", .{ inst, inst_tracking.* }); |
| 436 | 453 | } |
| 437 | 454 | |
| 438 | | fn resurrect(func: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void { |
| 439 | | switch (func.short) { |
| 455 | fn resurrect(inst_tracking: *InstTracking, inst: Air.Inst.Index, scope_generation: u32) void { |
| 456 | switch (inst_tracking.short) { |
| 440 | 457 | .dead => |die_generation| if (die_generation >= scope_generation) { |
| 441 | | func.reuseFrame(); |
| 442 | | tracking_log.debug("%{d} => {} (resurrect)", .{ inst, func.* }); |
| 458 | inst_tracking.reuseFrame(); |
| 459 | tracking_log.debug("%{d} => {} (resurrect)", .{ inst, inst_tracking.* }); |
| 443 | 460 | }, |
| 444 | 461 | else => {}, |
| 445 | 462 | } |
| 446 | 463 | } |
| 447 | 464 | |
| 448 | | fn die(func: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 449 | | if (func.short == .dead) return; |
| 450 | | try function.freeValue(func.short); |
| 451 | | func.short = .{ .dead = function.scope_generation }; |
| 452 | | tracking_log.debug("%{d} => {} (death)", .{ inst, func.* }); |
| 465 | fn die(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) !void { |
| 466 | if (inst_tracking.short == .dead) return; |
| 467 | try function.freeValue(inst_tracking.short); |
| 468 | inst_tracking.short = .{ .dead = function.scope_generation }; |
| 469 | tracking_log.debug("%{d} => {} (death)", .{ inst, inst_tracking.* }); |
| 453 | 470 | } |
| 454 | 471 | |
| 455 | 472 | fn reuse( |
| 456 | | func: *InstTracking, |
| 473 | inst_tracking: *InstTracking, |
| 457 | 474 | function: *Func, |
| 458 | 475 | new_inst: ?Air.Inst.Index, |
| 459 | 476 | old_inst: Air.Inst.Index, |
| 460 | 477 | ) void { |
| 461 | | func.short = .{ .dead = function.scope_generation }; |
| 478 | inst_tracking.short = .{ .dead = function.scope_generation }; |
| 462 | 479 | if (new_inst) |inst| |
| 463 | | tracking_log.debug("%{d} => {} (reuse %{d})", .{ inst, func.*, old_inst }) |
| 480 | tracking_log.debug("%{d} => {} (reuse %{d})", .{ inst, inst_tracking.*, old_inst }) |
| 464 | 481 | else |
| 465 | | tracking_log.debug("tmp => {} (reuse %{d})", .{ func.*, old_inst }); |
| 482 | tracking_log.debug("tmp => {} (reuse %{d})", .{ inst_tracking.*, old_inst }); |
| 466 | 483 | } |
| 467 | 484 | |
| 468 | | fn liveOut(func: *InstTracking, function: *Func, inst: Air.Inst.Index) void { |
| 469 | | for (func.getRegs()) |reg| { |
| 485 | fn liveOut(inst_tracking: *InstTracking, function: *Func, inst: Air.Inst.Index) void { |
| 486 | for (inst_tracking.getRegs()) |reg| { |
| 470 | 487 | if (function.register_manager.isRegFree(reg)) { |
| 471 | | tracking_log.debug("%{d} => {} (live-out)", .{ inst, func.* }); |
| 488 | tracking_log.debug("%{d} => {} (live-out)", .{ inst, inst_tracking.* }); |
| 472 | 489 | continue; |
| 473 | 490 | } |
| 474 | 491 | |
| ... | ... | @@ -495,18 +512,18 @@ const InstTracking = struct { |
| 495 | 512 | // Perform side-effects of freeValue manually. |
| 496 | 513 | function.register_manager.freeReg(reg); |
| 497 | 514 | |
| 498 | | tracking_log.debug("%{d} => {} (live-out %{d})", .{ inst, func.*, tracked_inst }); |
| 515 | tracking_log.debug("%{d} => {} (live-out %{d})", .{ inst, inst_tracking.*, tracked_inst }); |
| 499 | 516 | } |
| 500 | 517 | } |
| 501 | 518 | |
| 502 | 519 | pub fn format( |
| 503 | | func: InstTracking, |
| 520 | inst_tracking: InstTracking, |
| 504 | 521 | comptime _: []const u8, |
| 505 | 522 | _: std.fmt.FormatOptions, |
| 506 | 523 | writer: anytype, |
| 507 | 524 | ) @TypeOf(writer).Error!void { |
| 508 | | if (!std.meta.eql(func.long, func.short)) try writer.print("|{}| ", .{func.long}); |
| 509 | | try writer.print("{}", .{func.short}); |
| 525 | if (!std.meta.eql(inst_tracking.long, inst_tracking.short)) try writer.print("|{}| ", .{inst_tracking.long}); |
| 526 | try writer.print("{}", .{inst_tracking.short}); |
| 510 | 527 | } |
| 511 | 528 | }; |
| 512 | 529 | |
| ... | ... | @@ -741,6 +758,8 @@ pub fn generate( |
| 741 | 758 | function.mir_extra.deinit(gpa); |
| 742 | 759 | } |
| 743 | 760 | |
| 761 | wip_mir_log.debug("{}:", .{function.fmtDecl(func.owner_decl)}); |
| 762 | |
| 744 | 763 | try function.frame_allocs.resize(gpa, FrameIndex.named_count); |
| 745 | 764 | function.frame_allocs.set( |
| 746 | 765 | @intFromEnum(FrameIndex.stack_frame), |
| ... | ... | @@ -846,13 +865,133 @@ pub fn generate( |
| 846 | 865 | } |
| 847 | 866 | } |
| 848 | 867 | |
| 868 | const FormatWipMirData = struct { |
| 869 | func: *Func, |
| 870 | inst: Mir.Inst.Index, |
| 871 | }; |
| 872 | fn formatWipMir( |
| 873 | data: FormatWipMirData, |
| 874 | comptime _: []const u8, |
| 875 | _: std.fmt.FormatOptions, |
| 876 | writer: anytype, |
| 877 | ) @TypeOf(writer).Error!void { |
| 878 | const comp = data.func.bin_file.comp; |
| 879 | const mod = comp.root_mod; |
| 880 | var lower = Lower{ |
| 881 | .bin_file = data.func.bin_file, |
| 882 | .allocator = data.func.gpa, |
| 883 | .mir = .{ |
| 884 | .instructions = data.func.mir_instructions.slice(), |
| 885 | .extra = data.func.mir_extra.items, |
| 886 | .frame_locs = data.func.frame_locs.slice(), |
| 887 | }, |
| 888 | .cc = .Unspecified, |
| 889 | .src_loc = data.func.src_loc, |
| 890 | .output_mode = comp.config.output_mode, |
| 891 | .link_mode = comp.config.link_mode, |
| 892 | .pic = mod.pic, |
| 893 | }; |
| 894 | var first = true; |
| 895 | for ((lower.lowerMir(data.inst) catch |err| switch (err) { |
| 896 | error.LowerFail => { |
| 897 | defer { |
| 898 | lower.err_msg.?.deinit(data.func.gpa); |
| 899 | lower.err_msg = null; |
| 900 | } |
| 901 | try writer.writeAll(lower.err_msg.?.msg); |
| 902 | return; |
| 903 | }, |
| 904 | error.OutOfMemory, error.InvalidInstruction => |e| { |
| 905 | try writer.writeAll(switch (e) { |
| 906 | error.OutOfMemory => "Out of memory", |
| 907 | error.InvalidInstruction => "CodeGen failed to find a viable instruction.", |
| 908 | }); |
| 909 | return; |
| 910 | }, |
| 911 | else => |e| return e, |
| 912 | }).insts) |lowered_inst| { |
| 913 | if (!first) try writer.writeAll("\ndebug(wip_mir): "); |
| 914 | try writer.print(" | {}", .{lowered_inst}); |
| 915 | first = false; |
| 916 | } |
| 917 | } |
| 918 | fn fmtWipMir(func: *Func, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { |
| 919 | return .{ .data = .{ .func = func, .inst = inst } }; |
| 920 | } |
| 921 | |
| 922 | const FormatDeclData = struct { |
| 923 | mod: *Module, |
| 924 | decl_index: InternPool.DeclIndex, |
| 925 | }; |
| 926 | fn formatDecl( |
| 927 | data: FormatDeclData, |
| 928 | comptime _: []const u8, |
| 929 | _: std.fmt.FormatOptions, |
| 930 | writer: anytype, |
| 931 | ) @TypeOf(writer).Error!void { |
| 932 | try data.mod.declPtr(data.decl_index).renderFullyQualifiedName(data.mod, writer); |
| 933 | } |
| 934 | fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { |
| 935 | return .{ .data = .{ |
| 936 | .mod = func.bin_file.comp.module.?, |
| 937 | .decl_index = decl_index, |
| 938 | } }; |
| 939 | } |
| 940 | |
| 941 | const FormatAirData = struct { |
| 942 | func: *Func, |
| 943 | inst: Air.Inst.Index, |
| 944 | }; |
| 945 | fn formatAir( |
| 946 | data: FormatAirData, |
| 947 | comptime _: []const u8, |
| 948 | _: std.fmt.FormatOptions, |
| 949 | writer: anytype, |
| 950 | ) @TypeOf(writer).Error!void { |
| 951 | @import("../../print_air.zig").dumpInst( |
| 952 | data.inst, |
| 953 | data.func.bin_file.comp.module.?, |
| 954 | data.func.air, |
| 955 | data.func.liveness, |
| 956 | ); |
| 957 | } |
| 958 | fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { |
| 959 | return .{ .data = .{ .func = func, .inst = inst } }; |
| 960 | } |
| 961 | |
| 962 | const FormatTrackingData = struct { |
| 963 | func: *Func, |
| 964 | }; |
| 965 | fn formatTracking( |
| 966 | data: FormatTrackingData, |
| 967 | comptime _: []const u8, |
| 968 | _: std.fmt.FormatOptions, |
| 969 | writer: anytype, |
| 970 | ) @TypeOf(writer).Error!void { |
| 971 | var it = data.func.inst_tracking.iterator(); |
| 972 | while (it.next()) |entry| try writer.print("\n%{d} = {}", .{ entry.key_ptr.*, entry.value_ptr.* }); |
| 973 | } |
| 974 | fn fmtTracking(func: *Func) std.fmt.Formatter(formatTracking) { |
| 975 | return .{ .data = .{ .func = func } }; |
| 976 | } |
| 977 | |
| 849 | 978 | fn addInst(func: *Func, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| 850 | 979 | const gpa = func.gpa; |
| 851 | | |
| 852 | 980 | try func.mir_instructions.ensureUnusedCapacity(gpa, 1); |
| 853 | | |
| 854 | 981 | const result_index: Mir.Inst.Index = @intCast(func.mir_instructions.len); |
| 855 | 982 | func.mir_instructions.appendAssumeCapacity(inst); |
| 983 | if (inst.tag != .pseudo or switch (inst.ops) { |
| 984 | else => true, |
| 985 | .pseudo_dbg_prologue_end, |
| 986 | .pseudo_dbg_line_column, |
| 987 | .pseudo_dbg_epilogue_begin, |
| 988 | .pseudo_store_rm, |
| 989 | .pseudo_load_rm, |
| 990 | .pseudo_lea_rm, |
| 991 | .pseudo_mv, |
| 992 | .pseudo_dead, |
| 993 | => false, |
| 994 | }) wip_mir_log.debug("{}", .{func.fmtWipMir(result_index)}) else wip_mir_log.debug(" | uses-mem", .{}); |
| 856 | 995 | return result_index; |
| 857 | 996 | } |
| 858 | 997 | |
| ... | ... | @@ -979,7 +1118,7 @@ fn gen(func: *Func) !void { |
| 979 | 1118 | .r = .ra, |
| 980 | 1119 | .m = .{ |
| 981 | 1120 | .base = .{ .frame = .ret_addr }, |
| 982 | | .mod = .{ .rm = .{ .size = .dword } }, |
| 1121 | .mod = .{ .size = .dword, .unsigned = false }, |
| 983 | 1122 | }, |
| 984 | 1123 | } }, |
| 985 | 1124 | }); |
| ... | ... | @@ -990,7 +1129,7 @@ fn gen(func: *Func) !void { |
| 990 | 1129 | .r = .ra, |
| 991 | 1130 | .m = .{ |
| 992 | 1131 | .base = .{ .frame = .ret_addr }, |
| 993 | | .mod = .{ .rm = .{ .size = .dword } }, |
| 1132 | .mod = .{ .size = .dword, .unsigned = false }, |
| 994 | 1133 | }, |
| 995 | 1134 | } }, |
| 996 | 1135 | }); |
| ... | ... | @@ -1001,7 +1140,7 @@ fn gen(func: *Func) !void { |
| 1001 | 1140 | .r = .s0, |
| 1002 | 1141 | .m = .{ |
| 1003 | 1142 | .base = .{ .frame = .base_ptr }, |
| 1004 | | .mod = .{ .rm = .{ .size = .dword } }, |
| 1143 | .mod = .{ .size = .dword, .unsigned = false }, |
| 1005 | 1144 | }, |
| 1006 | 1145 | } }, |
| 1007 | 1146 | }); |
| ... | ... | @@ -1012,7 +1151,7 @@ fn gen(func: *Func) !void { |
| 1012 | 1151 | .r = .s0, |
| 1013 | 1152 | .m = .{ |
| 1014 | 1153 | .base = .{ .frame = .base_ptr }, |
| 1015 | | .mod = .{ .rm = .{ .size = .dword } }, |
| 1154 | .mod = .{ .size = .dword, .unsigned = false }, |
| 1016 | 1155 | }, |
| 1017 | 1156 | } }, |
| 1018 | 1157 | }); |
| ... | ... | @@ -1072,36 +1211,47 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1072 | 1211 | |
| 1073 | 1212 | for (body) |inst| { |
| 1074 | 1213 | if (func.liveness.isUnused(inst) and !func.air.mustLower(inst, ip)) continue; |
| 1214 | wip_mir_log.debug("{}", .{func.fmtAir(inst)}); |
| 1215 | verbose_tracking_log.debug("{}", .{func.fmtTracking()}); |
| 1075 | 1216 | |
| 1076 | 1217 | const old_air_bookkeeping = func.air_bookkeeping; |
| 1077 | 1218 | try func.inst_tracking.ensureUnusedCapacity(func.gpa, 1); |
| 1078 | | switch (air_tags[@intFromEnum(inst)]) { |
| 1219 | const tag: Air.Inst.Tag = air_tags[@intFromEnum(inst)]; |
| 1220 | switch (tag) { |
| 1079 | 1221 | // zig fmt: off |
| 1080 | | .ptr_add => try func.airPtrArithmetic(inst, .ptr_add), |
| 1081 | | .ptr_sub => try func.airPtrArithmetic(inst, .ptr_sub), |
| 1222 | .add, |
| 1223 | .add_wrap, |
| 1224 | .sub, |
| 1225 | .sub_wrap, |
| 1082 | 1226 | |
| 1083 | | .add => try func.airBinOp(inst, .add), |
| 1084 | | .sub => try func.airBinOp(inst, .sub), |
| 1227 | .mul, |
| 1228 | .mul_wrap, |
| 1229 | .div_trunc, |
| 1085 | 1230 | |
| 1086 | | .add_safe, |
| 1087 | | .sub_safe, |
| 1088 | | .mul_safe, |
| 1089 | | => return func.fail("TODO implement safety_checked_instructions", .{}), |
| 1231 | .shl, .shl_exact, |
| 1232 | .shr, .shr_exact, |
| 1090 | 1233 | |
| 1091 | | .add_wrap => try func.airAddWrap(inst), |
| 1092 | | .add_sat => try func.airAddSat(inst), |
| 1093 | | .sub_wrap => try func.airSubWrap(inst), |
| 1094 | | .sub_sat => try func.airSubSat(inst), |
| 1095 | | .mul => try func.airMul(inst), |
| 1096 | | .mul_wrap => try func.airMulWrap(inst), |
| 1097 | | .mul_sat => try func.airMulSat(inst), |
| 1098 | | .rem => try func.airRem(inst), |
| 1099 | | .mod => try func.airMod(inst), |
| 1100 | | .shl, .shl_exact => try func.airShl(inst), |
| 1101 | | .shl_sat => try func.airShlSat(inst), |
| 1102 | | .min => try func.airMinMax(inst, .min), |
| 1103 | | .max => try func.airMinMax(inst, .max), |
| 1104 | | .slice => try func.airSlice(inst), |
| 1234 | .bool_and, |
| 1235 | .bool_or, |
| 1236 | .bit_and, |
| 1237 | .bit_or, |
| 1238 | |
| 1239 | .xor, |
| 1240 | |
| 1241 | .min, |
| 1242 | .max, |
| 1243 | => try func.airBinOp(inst, tag), |
| 1244 | |
| 1245 | |
| 1246 | .ptr_add, |
| 1247 | .ptr_sub => try func.airPtrArithmetic(inst, tag), |
| 1248 | |
| 1249 | .rem, |
| 1250 | .mod, |
| 1251 | .div_float, |
| 1252 | .div_floor, |
| 1253 | .div_exact, |
| 1254 | => return func.fail("TODO: {s}", .{@tagName(tag)}), |
| 1105 | 1255 | |
| 1106 | 1256 | .sqrt, |
| 1107 | 1257 | .sin, |
| ... | ... | @@ -1124,24 +1274,33 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1124 | 1274 | .mul_with_overflow => try func.airMulWithOverflow(inst), |
| 1125 | 1275 | .shl_with_overflow => try func.airShlWithOverflow(inst), |
| 1126 | 1276 | |
| 1127 | | .div_float, .div_trunc, .div_floor, .div_exact => try func.airDiv(inst), |
| 1128 | 1277 | |
| 1129 | | .cmp_lt => try func.airCmp(inst), |
| 1130 | | .cmp_lte => try func.airCmp(inst), |
| 1131 | | .cmp_eq => try func.airCmp(inst), |
| 1132 | | .cmp_gte => try func.airCmp(inst), |
| 1133 | | .cmp_gt => try func.airCmp(inst), |
| 1134 | | .cmp_neq => try func.airCmp(inst), |
| 1278 | .add_sat => try func.airAddSat(inst), |
| 1279 | .sub_sat => try func.airSubSat(inst), |
| 1280 | .mul_sat => try func.airMulSat(inst), |
| 1281 | .shl_sat => try func.airShlSat(inst), |
| 1282 | |
| 1283 | .add_safe, |
| 1284 | .sub_safe, |
| 1285 | .mul_safe, |
| 1286 | => return func.fail("TODO implement safety_checked_instructions", .{}), |
| 1287 | |
| 1288 | .cmp_lt, |
| 1289 | .cmp_lte, |
| 1290 | .cmp_eq, |
| 1291 | .cmp_gte, |
| 1292 | .cmp_gt, |
| 1293 | .cmp_neq, |
| 1294 | => try func.airCmp(inst, tag), |
| 1135 | 1295 | |
| 1136 | 1296 | .cmp_vector => try func.airCmpVector(inst), |
| 1137 | 1297 | .cmp_lt_errors_len => try func.airCmpLtErrorsLen(inst), |
| 1138 | 1298 | |
| 1139 | | .bool_and => try func.airBoolOp(inst), |
| 1140 | | .bool_or => try func.airBoolOp(inst), |
| 1141 | | .bit_and => try func.airBitAnd(inst), |
| 1142 | | .bit_or => try func.airBitOr(inst), |
| 1143 | | .xor => try func.airXor(inst), |
| 1144 | | .shr, .shr_exact => try func.airShr(inst), |
| 1299 | .slice => try func.airSlice(inst), |
| 1300 | .array_to_slice => try func.airArrayToSlice(inst), |
| 1301 | |
| 1302 | .slice_ptr => try func.airSlicePtr(inst), |
| 1303 | .slice_len => try func.airSliceLen(inst), |
| 1145 | 1304 | |
| 1146 | 1305 | .alloc => try func.airAlloc(inst), |
| 1147 | 1306 | .ret_ptr => try func.airRetPtr(inst), |
| ... | ... | @@ -1181,7 +1340,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1181 | 1340 | .store_safe => try func.airStore(inst, true), |
| 1182 | 1341 | .struct_field_ptr=> try func.airStructFieldPtr(inst), |
| 1183 | 1342 | .struct_field_val=> try func.airStructFieldVal(inst), |
| 1184 | | .array_to_slice => try func.airArrayToSlice(inst), |
| 1185 | 1343 | .float_from_int => try func.airFloatFromInt(inst), |
| 1186 | 1344 | .int_from_float => try func.airIntFromFloat(inst), |
| 1187 | 1345 | .cmpxchg_strong => try func.airCmpxchg(inst), |
| ... | ... | @@ -1229,7 +1387,6 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1229 | 1387 | .atomic_store_monotonic => try func.airAtomicStore(inst, .monotonic), |
| 1230 | 1388 | .atomic_store_release => try func.airAtomicStore(inst, .release), |
| 1231 | 1389 | .atomic_store_seq_cst => try func.airAtomicStore(inst, .seq_cst), |
| 1232 | | |
| 1233 | 1390 | .struct_field_ptr_index_0 => try func.airStructFieldPtrIndex(inst, 0), |
| 1234 | 1391 | .struct_field_ptr_index_1 => try func.airStructFieldPtrIndex(inst, 1), |
| 1235 | 1392 | .struct_field_ptr_index_2 => try func.airStructFieldPtrIndex(inst, 2), |
| ... | ... | @@ -1238,15 +1395,15 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1238 | 1395 | .field_parent_ptr => try func.airFieldParentPtr(inst), |
| 1239 | 1396 | |
| 1240 | 1397 | .switch_br => try func.airSwitchBr(inst), |
| 1241 | | .slice_ptr => try func.airSlicePtr(inst), |
| 1242 | | .slice_len => try func.airSliceLen(inst), |
| 1243 | 1398 | |
| 1244 | 1399 | .ptr_slice_len_ptr => try func.airPtrSliceLenPtr(inst), |
| 1245 | 1400 | .ptr_slice_ptr_ptr => try func.airPtrSlicePtrPtr(inst), |
| 1246 | 1401 | |
| 1247 | 1402 | .array_elem_val => try func.airArrayElemVal(inst), |
| 1403 | |
| 1248 | 1404 | .slice_elem_val => try func.airSliceElemVal(inst), |
| 1249 | 1405 | .slice_elem_ptr => try func.airSliceElemPtr(inst), |
| 1406 | |
| 1250 | 1407 | .ptr_elem_val => try func.airPtrElemVal(inst), |
| 1251 | 1408 | .ptr_elem_ptr => try func.airPtrElemPtr(inst), |
| 1252 | 1409 | |
| ... | ... | @@ -1330,6 +1487,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void { |
| 1330 | 1487 | } |
| 1331 | 1488 | } |
| 1332 | 1489 | } |
| 1490 | verbose_tracking_log.debug("{}", .{func.fmtTracking()}); |
| 1333 | 1491 | } |
| 1334 | 1492 | |
| 1335 | 1493 | fn getValue(func: *Func, value: MCValue, inst: ?Air.Inst.Index) !void { |
| ... | ... | @@ -1563,7 +1721,7 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { |
| 1563 | 1721 | .i_type = .{ |
| 1564 | 1722 | .rd = reg, |
| 1565 | 1723 | .rs1 = reg, |
| 1566 | | .imm12 = Immediate.s(shift), |
| 1724 | .imm12 = Immediate.u(shift), |
| 1567 | 1725 | }, |
| 1568 | 1726 | }, |
| 1569 | 1727 | }); |
| ... | ... | @@ -1574,25 +1732,49 @@ fn truncateRegister(func: *Func, ty: Type, reg: Register) !void { |
| 1574 | 1732 | .i_type = .{ |
| 1575 | 1733 | .rd = reg, |
| 1576 | 1734 | .rs1 = reg, |
| 1577 | | .imm12 = Immediate.s(shift), |
| 1735 | .imm12 = Immediate.u(shift), |
| 1578 | 1736 | }, |
| 1579 | 1737 | }, |
| 1580 | 1738 | }); |
| 1581 | 1739 | }, |
| 1582 | 1740 | .unsigned => { |
| 1583 | 1741 | const mask = ~@as(u64, 0) >> shift; |
| 1584 | | const tmp_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 1585 | | _ = try func.addInst(.{ |
| 1586 | | .tag = .@"and", |
| 1587 | | .ops = .rrr, |
| 1588 | | .data = .{ |
| 1589 | | .r_type = .{ |
| 1590 | | .rd = reg, |
| 1591 | | .rs1 = reg, |
| 1592 | | .rs2 = tmp_reg, |
| 1742 | if (mask < 256) { |
| 1743 | _ = try func.addInst(.{ |
| 1744 | .tag = .andi, |
| 1745 | .ops = .rri, |
| 1746 | .data = .{ |
| 1747 | .i_type = .{ |
| 1748 | .rd = reg, |
| 1749 | .rs1 = reg, |
| 1750 | .imm12 = Immediate.u(@intCast(mask)), |
| 1751 | }, |
| 1593 | 1752 | }, |
| 1594 | | }, |
| 1595 | | }); |
| 1753 | }); |
| 1754 | } else { |
| 1755 | _ = try func.addInst(.{ |
| 1756 | .tag = .slli, |
| 1757 | .ops = .rri, |
| 1758 | .data = .{ |
| 1759 | .i_type = .{ |
| 1760 | .rd = reg, |
| 1761 | .rs1 = reg, |
| 1762 | .imm12 = Immediate.u(shift), |
| 1763 | }, |
| 1764 | }, |
| 1765 | }); |
| 1766 | _ = try func.addInst(.{ |
| 1767 | .tag = .srli, |
| 1768 | .ops = .rri, |
| 1769 | .data = .{ |
| 1770 | .i_type = .{ |
| 1771 | .rd = reg, |
| 1772 | .rs1 = reg, |
| 1773 | .imm12 = Immediate.u(shift), |
| 1774 | }, |
| 1775 | }, |
| 1776 | }); |
| 1777 | } |
| 1596 | 1778 | }, |
| 1597 | 1779 | } |
| 1598 | 1780 | } |
| ... | ... | @@ -1673,9 +1855,8 @@ fn regTempClassForType(func: *Func, ty: Type) RegisterManager.RegisterBitSet { |
| 1673 | 1855 | }; |
| 1674 | 1856 | } |
| 1675 | 1857 | |
| 1676 | | fn allocRegOrMem(func: *Func, inst: Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1858 | fn allocRegOrMem(func: *Func, elem_ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { |
| 1677 | 1859 | const zcu = func.bin_file.comp.module.?; |
| 1678 | | const elem_ty = func.typeOfIndex(inst); |
| 1679 | 1860 | |
| 1680 | 1861 | const abi_size = math.cast(u32, elem_ty.abiSize(zcu)) orelse { |
| 1681 | 1862 | return func.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(zcu)}); |
| ... | ... | @@ -1714,66 +1895,15 @@ fn allocReg(func: *Func, reg_class: abi.RegisterClass) !struct { Register, Regis |
| 1714 | 1895 | return .{ reg, lock }; |
| 1715 | 1896 | } |
| 1716 | 1897 | |
| 1717 | | const PromoteOptions = struct { |
| 1718 | | /// zeroes out the register before loading in the operand |
| 1719 | | /// |
| 1720 | | /// if the operand is already a register, it will truncate with 0 |
| 1721 | | zero: bool = false, |
| 1722 | | }; |
| 1723 | | |
| 1724 | 1898 | /// Similar to `allocReg` but will copy the MCValue into the Register unless `operand` is already |
| 1725 | 1899 | /// a register, in which case it will return a possible lock to that register. |
| 1726 | | fn promoteReg(func: *Func, ty: Type, operand: MCValue, options: PromoteOptions) !struct { Register, ?RegisterLock } { |
| 1727 | | const zcu = func.bin_file.comp.module.?; |
| 1728 | | const bit_size = ty.bitSize(zcu); |
| 1729 | | |
| 1900 | fn promoteReg(func: *Func, ty: Type, operand: MCValue) !struct { Register, ?RegisterLock } { |
| 1730 | 1901 | if (operand == .register) { |
| 1731 | 1902 | const op_reg = operand.register; |
| 1732 | | if (options.zero and op_reg.class() == .int) { |
| 1733 | | // we make sure to emit the truncate manually because binOp will call this function |
| 1734 | | // and it could cause an infinite loop |
| 1735 | | |
| 1736 | | _ = try func.addInst(.{ |
| 1737 | | .tag = .slli, |
| 1738 | | .ops = .rri, |
| 1739 | | .data = .{ |
| 1740 | | .i_type = .{ |
| 1741 | | .imm12 = Immediate.u(64 - bit_size), |
| 1742 | | .rd = op_reg, |
| 1743 | | .rs1 = op_reg, |
| 1744 | | }, |
| 1745 | | }, |
| 1746 | | }); |
| 1747 | | |
| 1748 | | _ = try func.addInst(.{ |
| 1749 | | .tag = .srli, |
| 1750 | | .ops = .rri, |
| 1751 | | .data = .{ |
| 1752 | | .i_type = .{ |
| 1753 | | .imm12 = Immediate.u(64 - bit_size), |
| 1754 | | .rd = op_reg, |
| 1755 | | .rs1 = op_reg, |
| 1756 | | }, |
| 1757 | | }, |
| 1758 | | }); |
| 1759 | | } |
| 1760 | | |
| 1761 | 1903 | return .{ op_reg, func.register_manager.lockReg(operand.register) }; |
| 1762 | 1904 | } |
| 1763 | 1905 | |
| 1764 | 1906 | const reg, const lock = try func.allocReg(func.typeRegClass(ty)); |
| 1765 | | |
| 1766 | | if (options.zero and reg.class() == .int) { |
| 1767 | | _ = try func.addInst(.{ |
| 1768 | | .tag = .pseudo, |
| 1769 | | .ops = .pseudo_mv, |
| 1770 | | .data = .{ .rr = .{ |
| 1771 | | .rd = reg, |
| 1772 | | .rs = .zero, |
| 1773 | | } }, |
| 1774 | | }); |
| 1775 | | } |
| 1776 | | |
| 1777 | 1907 | try func.genSetReg(ty, reg, operand); |
| 1778 | 1908 | return .{ reg, lock }; |
| 1779 | 1909 | } |
| ... | ... | @@ -1793,14 +1923,19 @@ fn elemOffset(func: *Func, index_ty: Type, index: MCValue, elem_size: u64) !Regi |
| 1793 | 1923 | const lock = func.register_manager.lockRegAssumeUnused(reg); |
| 1794 | 1924 | defer func.register_manager.unlockReg(lock); |
| 1795 | 1925 | |
| 1796 | | const result = try func.binOp( |
| 1926 | const result_reg, const result_lock = try func.allocReg(.int); |
| 1927 | defer func.register_manager.unlockReg(result_lock); |
| 1928 | |
| 1929 | try func.genBinOp( |
| 1797 | 1930 | .mul, |
| 1798 | 1931 | .{ .register = reg }, |
| 1799 | 1932 | index_ty, |
| 1800 | 1933 | .{ .immediate = elem_size }, |
| 1801 | 1934 | index_ty, |
| 1935 | result_reg, |
| 1802 | 1936 | ); |
| 1803 | | break :blk result.register; |
| 1937 | |
| 1938 | break :blk result_reg; |
| 1804 | 1939 | }, |
| 1805 | 1940 | } |
| 1806 | 1941 | }; |
| ... | ... | @@ -1892,7 +2027,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void { |
| 1892 | 2027 | math.divCeil(u16, dst_int_info.bits, 64) catch unreachable == |
| 1893 | 2028 | math.divCeil(u32, src_storage_bits, 64) catch unreachable and |
| 1894 | 2029 | func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 1895 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 2030 | const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true); |
| 1896 | 2031 | try func.genCopy(min_ty, dst_mcv, src_mcv); |
| 1897 | 2032 | break :dst dst_mcv; |
| 1898 | 2033 | }; |
| ... | ... | @@ -1904,7 +2039,7 @@ fn airIntCast(func: *Func, inst: Air.Inst.Index) !void { |
| 1904 | 2039 | break :result null; // TODO |
| 1905 | 2040 | |
| 1906 | 2041 | break :result dst_mcv; |
| 1907 | | } orelse return func.fail("TODO implement airIntCast from {} to {}", .{ |
| 2042 | } orelse return func.fail("TODO: implement airIntCast from {} to {}", .{ |
| 1908 | 2043 | src_ty.fmt(zcu), dst_ty.fmt(zcu), |
| 1909 | 2044 | }); |
| 1910 | 2045 | |
| ... | ... | @@ -1948,7 +2083,7 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { |
| 1948 | 2083 | if (func.reuseOperand(inst, ty_op.operand, 0, operand) and operand == .register) |
| 1949 | 2084 | operand.register |
| 1950 | 2085 | else |
| 1951 | | (try func.allocRegOrMem(inst, true)).register; |
| 2086 | (try func.allocRegOrMem(func.typeOfIndex(inst), inst, true)).register; |
| 1952 | 2087 | |
| 1953 | 2088 | _ = try func.addInst(.{ |
| 1954 | 2089 | .tag = .pseudo, |
| ... | ... | @@ -1970,106 +2105,6 @@ fn airNot(func: *Func, inst: Air.Inst.Index) !void { |
| 1970 | 2105 | return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1971 | 2106 | } |
| 1972 | 2107 | |
| 1973 | | fn airMinMax( |
| 1974 | | func: *Func, |
| 1975 | | inst: Air.Inst.Index, |
| 1976 | | comptime tag: enum { |
| 1977 | | max, |
| 1978 | | min, |
| 1979 | | }, |
| 1980 | | ) !void { |
| 1981 | | const zcu = func.bin_file.comp.module.?; |
| 1982 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1983 | | |
| 1984 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 1985 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 1986 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 1987 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 1988 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 1989 | | |
| 1990 | | const int_info = lhs_ty.intInfo(zcu); |
| 1991 | | |
| 1992 | | if (int_info.bits > 64) return func.fail("TODO: > 64 bit @min", .{}); |
| 1993 | | |
| 1994 | | const lhs_reg, const lhs_lock = blk: { |
| 1995 | | if (lhs == .register) break :blk .{ lhs.register, func.register_manager.lockReg(lhs.register) }; |
| 1996 | | |
| 1997 | | const lhs_reg, const lhs_lock = try func.allocReg(.int); |
| 1998 | | try func.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1999 | | break :blk .{ lhs_reg, lhs_lock }; |
| 2000 | | }; |
| 2001 | | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2002 | | |
| 2003 | | const rhs_reg, const rhs_lock = blk: { |
| 2004 | | if (rhs == .register) break :blk .{ rhs.register, func.register_manager.lockReg(rhs.register) }; |
| 2005 | | |
| 2006 | | const rhs_reg, const rhs_lock = try func.allocReg(.int); |
| 2007 | | try func.genSetReg(rhs_ty, rhs_reg, rhs); |
| 2008 | | break :blk .{ rhs_reg, rhs_lock }; |
| 2009 | | }; |
| 2010 | | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2011 | | |
| 2012 | | const mask_reg, const mask_lock = try func.allocReg(.int); |
| 2013 | | defer func.register_manager.unlockReg(mask_lock); |
| 2014 | | |
| 2015 | | const result_reg, const result_lock = try func.allocReg(.int); |
| 2016 | | defer func.register_manager.unlockReg(result_lock); |
| 2017 | | |
| 2018 | | _ = try func.addInst(.{ |
| 2019 | | .tag = if (int_info.signedness == .unsigned) .sltu else .slt, |
| 2020 | | .ops = .rrr, |
| 2021 | | .data = .{ .r_type = .{ |
| 2022 | | .rd = mask_reg, |
| 2023 | | .rs1 = lhs_reg, |
| 2024 | | .rs2 = rhs_reg, |
| 2025 | | } }, |
| 2026 | | }); |
| 2027 | | |
| 2028 | | _ = try func.addInst(.{ |
| 2029 | | .tag = .sub, |
| 2030 | | .ops = .rrr, |
| 2031 | | .data = .{ .r_type = .{ |
| 2032 | | .rd = mask_reg, |
| 2033 | | .rs1 = .zero, |
| 2034 | | .rs2 = mask_reg, |
| 2035 | | } }, |
| 2036 | | }); |
| 2037 | | |
| 2038 | | _ = try func.addInst(.{ |
| 2039 | | .tag = .xor, |
| 2040 | | .ops = .rrr, |
| 2041 | | .data = .{ .r_type = .{ |
| 2042 | | .rd = result_reg, |
| 2043 | | .rs1 = lhs_reg, |
| 2044 | | .rs2 = rhs_reg, |
| 2045 | | } }, |
| 2046 | | }); |
| 2047 | | |
| 2048 | | _ = try func.addInst(.{ |
| 2049 | | .tag = .@"and", |
| 2050 | | .ops = .rrr, |
| 2051 | | .data = .{ .r_type = .{ |
| 2052 | | .rd = mask_reg, |
| 2053 | | .rs1 = result_reg, |
| 2054 | | .rs2 = mask_reg, |
| 2055 | | } }, |
| 2056 | | }); |
| 2057 | | |
| 2058 | | _ = try func.addInst(.{ |
| 2059 | | .tag = .xor, |
| 2060 | | .ops = .rrr, |
| 2061 | | .data = .{ .r_type = .{ |
| 2062 | | .rd = result_reg, |
| 2063 | | .rs1 = if (tag == .min) rhs_reg else lhs_reg, |
| 2064 | | .rs2 = mask_reg, |
| 2065 | | } }, |
| 2066 | | }); |
| 2067 | | |
| 2068 | | break :result .{ .register = result_reg }; |
| 2069 | | }; |
| 2070 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2071 | | } |
| 2072 | | |
| 2073 | 2108 | fn airSlice(func: *Func, inst: Air.Inst.Index) !void { |
| 2074 | 2109 | const zcu = func.bin_file.comp.module.?; |
| 2075 | 2110 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| ... | ... | @@ -2094,417 +2129,487 @@ fn airSlice(func: *Func, inst: Air.Inst.Index) !void { |
| 2094 | 2129 | } |
| 2095 | 2130 | |
| 2096 | 2131 | fn airBinOp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 2132 | const zcu = func.bin_file.comp.module.?; |
| 2097 | 2133 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2098 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2099 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2100 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2101 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2134 | const dst_mcv = try func.binOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 2102 | 2135 | |
| 2103 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2104 | | break :result try func.binOp(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2105 | | }; |
| 2106 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2136 | const dst_ty = func.typeOfIndex(inst); |
| 2137 | if (dst_ty.isAbiInt(zcu)) { |
| 2138 | const abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); |
| 2139 | const bit_size: u32 = @intCast(dst_ty.bitSize(zcu)); |
| 2140 | if (abi_size * 8 > bit_size) { |
| 2141 | const dst_lock = switch (dst_mcv) { |
| 2142 | .register => |dst_reg| func.register_manager.lockRegAssumeUnused(dst_reg), |
| 2143 | else => null, |
| 2144 | }; |
| 2145 | defer if (dst_lock) |lock| func.register_manager.unlockReg(lock); |
| 2146 | |
| 2147 | if (dst_mcv.isRegister()) { |
| 2148 | try func.truncateRegister(dst_ty, dst_mcv.getReg().?); |
| 2149 | } else { |
| 2150 | const tmp_reg, const tmp_lock = try func.allocReg(.int); |
| 2151 | defer func.register_manager.unlockReg(tmp_lock); |
| 2152 | |
| 2153 | const hi_ty = try zcu.intType(.unsigned, @intCast((dst_ty.bitSize(zcu) - 1) % 64 + 1)); |
| 2154 | const hi_mcv = dst_mcv.address().offset(@intCast(bit_size / 64 * 8)).deref(); |
| 2155 | try func.genSetReg(hi_ty, tmp_reg, hi_mcv); |
| 2156 | try func.truncateRegister(dst_ty, tmp_reg); |
| 2157 | try func.genCopy(hi_ty, hi_mcv, .{ .register = tmp_reg }); |
| 2158 | } |
| 2159 | } |
| 2160 | } |
| 2161 | |
| 2162 | return func.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2107 | 2163 | } |
| 2108 | 2164 | |
| 2109 | 2165 | fn binOp( |
| 2166 | func: *Func, |
| 2167 | maybe_inst: ?Air.Inst.Index, |
| 2168 | air_tag: Air.Inst.Tag, |
| 2169 | lhs_air: Air.Inst.Ref, |
| 2170 | rhs_air: Air.Inst.Ref, |
| 2171 | ) !MCValue { |
| 2172 | _ = maybe_inst; |
| 2173 | const zcu = func.bin_file.comp.module.?; |
| 2174 | const lhs_ty = func.typeOf(lhs_air); |
| 2175 | const rhs_ty = func.typeOf(rhs_air); |
| 2176 | |
| 2177 | if (lhs_ty.isRuntimeFloat()) libcall: { |
| 2178 | const float_bits = lhs_ty.floatBits(func.target.*); |
| 2179 | const type_needs_libcall = switch (float_bits) { |
| 2180 | 16 => true, |
| 2181 | 32, 64 => false, |
| 2182 | 80, 128 => true, |
| 2183 | else => unreachable, |
| 2184 | }; |
| 2185 | switch (air_tag) { |
| 2186 | .rem, .mod => {}, |
| 2187 | else => if (!type_needs_libcall) break :libcall, |
| 2188 | } |
| 2189 | return func.fail("binOp libcall runtime-float ops", .{}); |
| 2190 | } |
| 2191 | |
| 2192 | if (lhs_ty.bitSize(zcu) > 64) return func.fail("TODO: binOp >= 64 bits", .{}); |
| 2193 | |
| 2194 | const lhs_mcv = try func.resolveInst(lhs_air); |
| 2195 | const rhs_mcv = try func.resolveInst(rhs_air); |
| 2196 | |
| 2197 | const class_for_dst_ty: abi.RegisterClass = switch (air_tag) { |
| 2198 | // will always return int register no matter the input |
| 2199 | .cmp_eq, |
| 2200 | .cmp_neq, |
| 2201 | .cmp_lt, |
| 2202 | .cmp_lte, |
| 2203 | .cmp_gt, |
| 2204 | .cmp_gte, |
| 2205 | => .int, |
| 2206 | |
| 2207 | else => func.typeRegClass(lhs_ty), |
| 2208 | }; |
| 2209 | |
| 2210 | const dst_reg, const dst_lock = try func.allocReg(class_for_dst_ty); |
| 2211 | defer func.register_manager.unlockReg(dst_lock); |
| 2212 | |
| 2213 | try func.genBinOp( |
| 2214 | air_tag, |
| 2215 | lhs_mcv, |
| 2216 | lhs_ty, |
| 2217 | rhs_mcv, |
| 2218 | rhs_ty, |
| 2219 | dst_reg, |
| 2220 | ); |
| 2221 | |
| 2222 | return .{ .register = dst_reg }; |
| 2223 | } |
| 2224 | |
| 2225 | /// Does the same thing as binOp however is meant to be used internally to the backend. |
| 2226 | /// |
| 2227 | /// The `dst_reg` argument is meant to be caller-locked. Asserts that the binOp result can be |
| 2228 | /// fit into the register. |
| 2229 | /// |
| 2230 | /// Assumes that the `dst_reg` class is correct. |
| 2231 | fn genBinOp( |
| 2110 | 2232 | func: *Func, |
| 2111 | 2233 | tag: Air.Inst.Tag, |
| 2112 | | lhs: MCValue, |
| 2234 | lhs_mcv: MCValue, |
| 2113 | 2235 | lhs_ty: Type, |
| 2114 | | rhs: MCValue, |
| 2236 | rhs_mcv: MCValue, |
| 2115 | 2237 | rhs_ty: Type, |
| 2116 | | ) InnerError!MCValue { |
| 2238 | dst_reg: Register, |
| 2239 | ) !void { |
| 2117 | 2240 | const zcu = func.bin_file.comp.module.?; |
| 2241 | const bit_size = lhs_ty.bitSize(zcu); |
| 2242 | assert(bit_size <= 64); |
| 2243 | |
| 2244 | const is_unsigned = lhs_ty.isUnsignedInt(zcu); |
| 2245 | |
| 2246 | const lhs_reg, const maybe_lhs_lock = try func.promoteReg(lhs_ty, lhs_mcv); |
| 2247 | const rhs_reg, const maybe_rhs_lock = try func.promoteReg(rhs_ty, rhs_mcv); |
| 2248 | |
| 2249 | defer if (maybe_lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2250 | defer if (maybe_rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2118 | 2251 | |
| 2119 | 2252 | switch (tag) { |
| 2120 | | // Arithmetic operations on integers and floats |
| 2121 | 2253 | .add, |
| 2254 | .add_wrap, |
| 2122 | 2255 | .sub, |
| 2256 | .sub_wrap, |
| 2123 | 2257 | .mul, |
| 2124 | | .div_float, |
| 2125 | | .cmp_eq, |
| 2126 | | .cmp_neq, |
| 2127 | | .cmp_gt, |
| 2128 | | .cmp_gte, |
| 2129 | | .cmp_lt, |
| 2130 | | .cmp_lte, |
| 2258 | .mul_wrap, |
| 2131 | 2259 | => { |
| 2132 | | assert(lhs_ty.eql(rhs_ty, zcu)); |
| 2133 | | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2134 | | .Float => { |
| 2135 | | const float_bits = lhs_ty.floatBits(zcu.getTarget()); |
| 2136 | | const float_reg_bits: u32 = if (func.hasFeature(.d)) 64 else 32; |
| 2137 | | if (float_bits <= float_reg_bits) { |
| 2138 | | return func.binOpFloat(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2139 | | } else { |
| 2140 | | return func.fail("TODO: binary operations for floats with bits > {d}", .{float_reg_bits}); |
| 2141 | | } |
| 2142 | | }, |
| 2143 | | .Vector => return func.fail("TODO binary operations on vectors", .{}), |
| 2144 | | .Int, .Enum, .ErrorSet => { |
| 2145 | | const int_info = lhs_ty.intInfo(zcu); |
| 2146 | | if (int_info.bits <= 64) { |
| 2147 | | return func.binOpRegister(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2148 | | } else { |
| 2149 | | return func.fail("TODO binary operations on int with bits > 64", .{}); |
| 2150 | | } |
| 2151 | | }, |
| 2152 | | else => |x| return func.fail("TOOD: binOp {s}", .{@tagName(x)}), |
| 2153 | | } |
| 2154 | | }, |
| 2260 | if (!math.isPowerOfTwo(bit_size)) |
| 2261 | return func.fail( |
| 2262 | "TODO: genBinOp {s} non-pow 2, found {}", |
| 2263 | .{ @tagName(tag), bit_size }, |
| 2264 | ); |
| 2155 | 2265 | |
| 2156 | | .ptr_add, |
| 2157 | | .ptr_sub, |
| 2158 | | => { |
| 2159 | 2266 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2160 | | .Pointer => { |
| 2161 | | const ptr_ty = lhs_ty; |
| 2162 | | const elem_ty = switch (ptr_ty.ptrSize(zcu)) { |
| 2163 | | .One => ptr_ty.childType(zcu).childType(zcu), // ptr to array, so get array element type |
| 2164 | | else => ptr_ty.childType(zcu), |
| 2165 | | }; |
| 2166 | | const elem_size = elem_ty.abiSize(zcu); |
| 2167 | | |
| 2168 | | if (elem_size == 1) { |
| 2169 | | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2170 | | .ptr_add => .add, |
| 2171 | | .ptr_sub => .sub, |
| 2267 | .Int => { |
| 2268 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2269 | .add, .add_wrap => switch (bit_size) { |
| 2270 | 8, 16, 64 => .add, |
| 2271 | 32 => .addw, |
| 2172 | 2272 | else => unreachable, |
| 2173 | | }; |
| 2273 | }, |
| 2274 | .sub, .sub_wrap => switch (bit_size) { |
| 2275 | 8, 16, 32 => .subw, |
| 2276 | 64 => .sub, |
| 2277 | else => unreachable, |
| 2278 | }, |
| 2279 | .mul, .mul_wrap => switch (bit_size) { |
| 2280 | 8, 16, 64 => .mul, |
| 2281 | 32 => .mulw, |
| 2282 | else => unreachable, |
| 2283 | }, |
| 2284 | else => unreachable, |
| 2285 | }; |
| 2174 | 2286 | |
| 2175 | | return try func.binOpRegister(base_tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2176 | | } else { |
| 2177 | | const offset = try func.binOp( |
| 2178 | | .mul, |
| 2179 | | rhs, |
| 2180 | | Type.usize, |
| 2181 | | .{ .immediate = elem_size }, |
| 2182 | | Type.usize, |
| 2183 | | ); |
| 2287 | _ = try func.addInst(.{ |
| 2288 | .tag = mir_tag, |
| 2289 | .ops = .rrr, |
| 2290 | .data = .{ |
| 2291 | .r_type = .{ |
| 2292 | .rd = dst_reg, |
| 2293 | .rs1 = lhs_reg, |
| 2294 | .rs2 = rhs_reg, |
| 2295 | }, |
| 2296 | }, |
| 2297 | }); |
| 2184 | 2298 | |
| 2185 | | const addr = try func.binOp( |
| 2186 | | tag, |
| 2187 | | lhs, |
| 2188 | | Type.manyptr_u8, |
| 2189 | | offset, |
| 2190 | | Type.usize, |
| 2191 | | ); |
| 2192 | | return addr; |
| 2299 | // truncate when the instruction is larger than the bit size. |
| 2300 | switch (bit_size) { |
| 2301 | 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), |
| 2302 | 32 => {}, // addw/subw affects the first 32-bits |
| 2303 | 64 => {}, // add/sub affects the entire register |
| 2304 | else => unreachable, |
| 2193 | 2305 | } |
| 2194 | 2306 | }, |
| 2195 | | else => unreachable, |
| 2196 | | } |
| 2197 | | }, |
| 2307 | .Float => { |
| 2308 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2309 | .add => switch (bit_size) { |
| 2310 | 32 => .fadds, |
| 2311 | 64 => .faddd, |
| 2312 | else => unreachable, |
| 2313 | }, |
| 2314 | .sub => switch (bit_size) { |
| 2315 | 32 => .fsubs, |
| 2316 | 64 => .fsubd, |
| 2317 | else => unreachable, |
| 2318 | }, |
| 2319 | .mul => switch (bit_size) { |
| 2320 | 32 => .fmuls, |
| 2321 | 64 => .fmuld, |
| 2322 | else => unreachable, |
| 2323 | }, |
| 2324 | else => unreachable, |
| 2325 | }; |
| 2198 | 2326 | |
| 2199 | | // These instructions have unsymteric bit sizes on RHS and LHS. |
| 2200 | | .shr, |
| 2201 | | .shl, |
| 2202 | | => { |
| 2203 | | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2204 | | .Float => return func.fail("TODO binary operations on floats", .{}), |
| 2205 | | .Vector => return func.fail("TODO binary operations on vectors", .{}), |
| 2206 | | .Int => { |
| 2207 | | const int_info = lhs_ty.intInfo(zcu); |
| 2208 | | if (int_info.bits <= 64) { |
| 2209 | | return func.binOpRegister(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2210 | | } else { |
| 2211 | | return func.fail("TODO binary operations on int with bits > 64", .{}); |
| 2212 | | } |
| 2327 | _ = try func.addInst(.{ |
| 2328 | .tag = mir_tag, |
| 2329 | .ops = .rrr, |
| 2330 | .data = .{ |
| 2331 | .r_type = .{ |
| 2332 | .rd = dst_reg, |
| 2333 | .rs1 = lhs_reg, |
| 2334 | .rs2 = rhs_reg, |
| 2335 | }, |
| 2336 | }, |
| 2337 | }); |
| 2213 | 2338 | }, |
| 2214 | 2339 | else => unreachable, |
| 2215 | 2340 | } |
| 2216 | 2341 | }, |
| 2217 | | else => return func.fail("TODO binOp {}", .{tag}), |
| 2218 | | } |
| 2219 | | } |
| 2220 | | |
| 2221 | | fn binOpRegister( |
| 2222 | | func: *Func, |
| 2223 | | tag: Air.Inst.Tag, |
| 2224 | | lhs: MCValue, |
| 2225 | | lhs_ty: Type, |
| 2226 | | rhs: MCValue, |
| 2227 | | rhs_ty: Type, |
| 2228 | | ) !MCValue { |
| 2229 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{ .zero = true }); |
| 2230 | | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2231 | | |
| 2232 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{ .zero = true }); |
| 2233 | | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2234 | 2342 | |
| 2235 | | const dest_reg, const dest_lock = try func.allocReg(.int); |
| 2236 | | defer func.register_manager.unlockReg(dest_lock); |
| 2237 | | |
| 2238 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2239 | | .add => .add, |
| 2240 | | .sub => .sub, |
| 2241 | | .mul => .mul, |
| 2242 | | |
| 2243 | | .shl => .sllw, |
| 2244 | | .shr => .srlw, |
| 2245 | | |
| 2246 | | .cmp_eq, |
| 2247 | | .cmp_neq, |
| 2248 | | .cmp_gt, |
| 2249 | | .cmp_gte, |
| 2250 | | .cmp_lt, |
| 2251 | | .cmp_lte, |
| 2252 | | => .pseudo, |
| 2343 | .ptr_add, |
| 2344 | .ptr_sub, |
| 2345 | => { |
| 2346 | const tmp_reg = try func.copyToTmpRegister(rhs_ty, .{ .register = rhs_reg }); |
| 2347 | const tmp_mcv = MCValue{ .register = tmp_reg }; |
| 2348 | const tmp_lock = func.register_manager.lockRegAssumeUnused(tmp_reg); |
| 2349 | defer func.register_manager.unlockReg(tmp_lock); |
| 2350 | |
| 2351 | // RISC-V has no immediate mul, so we copy the size to a temporary register |
| 2352 | const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); |
| 2353 | const elem_size_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = elem_size }); |
| 2354 | |
| 2355 | try func.genBinOp( |
| 2356 | .mul, |
| 2357 | tmp_mcv, |
| 2358 | rhs_ty, |
| 2359 | .{ .register = elem_size_reg }, |
| 2360 | Type.usize, |
| 2361 | tmp_reg, |
| 2362 | ); |
| 2253 | 2363 | |
| 2254 | | else => return func.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), |
| 2255 | | }; |
| 2364 | try func.genBinOp( |
| 2365 | switch (tag) { |
| 2366 | .ptr_add => .add, |
| 2367 | .ptr_sub => .sub, |
| 2368 | else => unreachable, |
| 2369 | }, |
| 2370 | lhs_mcv, |
| 2371 | Type.usize, // we know it's a pointer, so it'll be usize. |
| 2372 | tmp_mcv, |
| 2373 | Type.usize, |
| 2374 | dst_reg, |
| 2375 | ); |
| 2376 | }, |
| 2256 | 2377 | |
| 2257 | | switch (mir_tag) { |
| 2258 | | .add, |
| 2259 | | .sub, |
| 2260 | | .mul, |
| 2261 | | .sllw, |
| 2262 | | .srlw, |
| 2378 | .bit_and, |
| 2379 | .bit_or, |
| 2380 | .bool_and, |
| 2381 | .bool_or, |
| 2263 | 2382 | => { |
| 2264 | 2383 | _ = try func.addInst(.{ |
| 2265 | | .tag = mir_tag, |
| 2384 | .tag = switch (tag) { |
| 2385 | .bit_and, .bool_and => .@"and", |
| 2386 | .bit_or, .bool_or => .@"or", |
| 2387 | else => unreachable, |
| 2388 | }, |
| 2266 | 2389 | .ops = .rrr, |
| 2267 | 2390 | .data = .{ |
| 2268 | 2391 | .r_type = .{ |
| 2269 | | .rd = dest_reg, |
| 2392 | .rd = dst_reg, |
| 2270 | 2393 | .rs1 = lhs_reg, |
| 2271 | 2394 | .rs2 = rhs_reg, |
| 2272 | 2395 | }, |
| 2273 | 2396 | }, |
| 2274 | 2397 | }); |
| 2398 | |
| 2399 | switch (tag) { |
| 2400 | .bool_and, |
| 2401 | .bool_or, |
| 2402 | => try func.truncateRegister(Type.bool, dst_reg), |
| 2403 | else => {}, |
| 2404 | } |
| 2275 | 2405 | }, |
| 2276 | 2406 | |
| 2277 | | .pseudo => { |
| 2278 | | const pseudo_op = switch (tag) { |
| 2279 | | .cmp_eq, |
| 2280 | | .cmp_neq, |
| 2281 | | .cmp_gt, |
| 2282 | | .cmp_gte, |
| 2283 | | .cmp_lt, |
| 2284 | | .cmp_lte, |
| 2285 | | => .pseudo_compare, |
| 2407 | .div_trunc, |
| 2408 | => { |
| 2409 | if (!math.isPowerOfTwo(bit_size)) |
| 2410 | return func.fail( |
| 2411 | "TODO: genBinOp {s} non-pow 2, found {}", |
| 2412 | .{ @tagName(tag), bit_size }, |
| 2413 | ); |
| 2414 | |
| 2415 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2416 | .div_trunc => switch (bit_size) { |
| 2417 | 8, 16, 32 => if (is_unsigned) .divuw else .divw, |
| 2418 | 64 => if (is_unsigned) .divu else .div, |
| 2419 | else => unreachable, |
| 2420 | }, |
| 2286 | 2421 | else => unreachable, |
| 2287 | 2422 | }; |
| 2288 | 2423 | |
| 2289 | 2424 | _ = try func.addInst(.{ |
| 2290 | | .tag = .pseudo, |
| 2291 | | .ops = pseudo_op, |
| 2425 | .tag = mir_tag, |
| 2426 | .ops = .rrr, |
| 2292 | 2427 | .data = .{ |
| 2293 | | .compare = .{ |
| 2294 | | .rd = dest_reg, |
| 2428 | .r_type = .{ |
| 2429 | .rd = dst_reg, |
| 2295 | 2430 | .rs1 = lhs_reg, |
| 2296 | 2431 | .rs2 = rhs_reg, |
| 2297 | | .op = switch (tag) { |
| 2298 | | .cmp_eq => .eq, |
| 2299 | | .cmp_neq => .neq, |
| 2300 | | .cmp_gt => .gt, |
| 2301 | | .cmp_gte => .gte, |
| 2302 | | .cmp_lt => .lt, |
| 2303 | | .cmp_lte => .lte, |
| 2304 | | else => unreachable, |
| 2305 | | }, |
| 2306 | | .size = func.memSize(lhs_ty), |
| 2307 | 2432 | }, |
| 2308 | 2433 | }, |
| 2309 | 2434 | }); |
| 2310 | | }, |
| 2311 | | |
| 2312 | | else => unreachable, |
| 2313 | | } |
| 2314 | | |
| 2315 | | return MCValue{ .register = dest_reg }; |
| 2316 | | } |
| 2317 | | |
| 2318 | | fn binOpFloat( |
| 2319 | | func: *Func, |
| 2320 | | tag: Air.Inst.Tag, |
| 2321 | | lhs: MCValue, |
| 2322 | | lhs_ty: Type, |
| 2323 | | rhs: MCValue, |
| 2324 | | rhs_ty: Type, |
| 2325 | | ) !MCValue { |
| 2326 | | const zcu = func.bin_file.comp.module.?; |
| 2327 | | const float_bits = lhs_ty.floatBits(zcu.getTarget()); |
| 2328 | | |
| 2329 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 2330 | | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2331 | | |
| 2332 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{}); |
| 2333 | | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2334 | | |
| 2335 | | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2336 | | .add => if (float_bits == 32) .fadds else .faddd, |
| 2337 | | .sub => if (float_bits == 32) .fsubs else .fsubd, |
| 2338 | | .mul => if (float_bits == 32) .fmuls else .fmuld, |
| 2339 | | .div_float => if (float_bits == 32) .fdivs else .fdivd, |
| 2340 | 2435 | |
| 2341 | | .cmp_eq, |
| 2342 | | .cmp_neq, |
| 2343 | | .cmp_gt, |
| 2344 | | .cmp_gte, |
| 2345 | | .cmp_lt, |
| 2346 | | .cmp_lte, |
| 2347 | | => .pseudo, |
| 2348 | | |
| 2349 | | else => return func.fail("TODO: binOpFloat mir_tag {s}", .{@tagName(tag)}), |
| 2350 | | }; |
| 2436 | if (!is_unsigned) { |
| 2437 | // truncate when the instruction is larger than the bit size. |
| 2438 | switch (bit_size) { |
| 2439 | 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), |
| 2440 | 32 => {}, // divw affects the first 32-bits |
| 2441 | 64 => {}, // div affects the entire register |
| 2442 | else => unreachable, |
| 2443 | } |
| 2444 | } |
| 2445 | }, |
| 2351 | 2446 | |
| 2352 | | const return_class: abi.RegisterClass = switch (tag) { |
| 2353 | | .add, |
| 2354 | | .sub, |
| 2355 | | .mul, |
| 2356 | | .div_float, |
| 2357 | | => .float, |
| 2447 | .shr, |
| 2448 | .shr_exact, |
| 2449 | .shl, |
| 2450 | .shl_exact, |
| 2451 | => { |
| 2452 | if (!math.isPowerOfTwo(bit_size)) |
| 2453 | return func.fail( |
| 2454 | "TODO: genBinOp {s} non-pow 2, found {}", |
| 2455 | .{ @tagName(tag), bit_size }, |
| 2456 | ); |
| 2358 | 2457 | |
| 2359 | | .cmp_eq, |
| 2360 | | .cmp_neq, |
| 2361 | | .cmp_gt, |
| 2362 | | .cmp_gte, |
| 2363 | | .cmp_lt, |
| 2364 | | .cmp_lte, |
| 2365 | | => .int, |
| 2366 | | else => unreachable, |
| 2367 | | }; |
| 2458 | // it's important that the shift amount is exact |
| 2459 | try func.truncateRegister(rhs_ty, rhs_reg); |
| 2368 | 2460 | |
| 2369 | | const dest_reg, const dest_lock = try func.allocReg(return_class); |
| 2370 | | defer func.register_manager.unlockReg(dest_lock); |
| 2461 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 2462 | .shl, .shl_exact => switch (bit_size) { |
| 2463 | 8, 16, 64 => .sll, |
| 2464 | 32 => .sllw, |
| 2465 | else => unreachable, |
| 2466 | }, |
| 2467 | .shr, .shr_exact => switch (bit_size) { |
| 2468 | 8, 16, 64 => .srl, |
| 2469 | 32 => .srlw, |
| 2470 | else => unreachable, |
| 2471 | }, |
| 2472 | else => unreachable, |
| 2473 | }; |
| 2371 | 2474 | |
| 2372 | | switch (tag) { |
| 2373 | | .add, |
| 2374 | | .sub, |
| 2375 | | .mul, |
| 2376 | | .div_float, |
| 2377 | | => { |
| 2378 | 2475 | _ = try func.addInst(.{ |
| 2379 | 2476 | .tag = mir_tag, |
| 2380 | 2477 | .ops = .rrr, |
| 2381 | 2478 | .data = .{ .r_type = .{ |
| 2382 | | .rd = dest_reg, |
| 2479 | .rd = dst_reg, |
| 2383 | 2480 | .rs1 = lhs_reg, |
| 2384 | 2481 | .rs2 = rhs_reg, |
| 2385 | 2482 | } }, |
| 2386 | 2483 | }); |
| 2484 | |
| 2485 | switch (bit_size) { |
| 2486 | 8, 16 => try func.truncateRegister(lhs_ty, dst_reg), |
| 2487 | 32 => {}, |
| 2488 | 64 => {}, |
| 2489 | else => unreachable, |
| 2490 | } |
| 2387 | 2491 | }, |
| 2388 | 2492 | |
| 2493 | // TODO: move the isel logic out of lower and into here. |
| 2389 | 2494 | .cmp_eq, |
| 2390 | 2495 | .cmp_neq, |
| 2391 | | .cmp_gt, |
| 2392 | | .cmp_gte, |
| 2393 | 2496 | .cmp_lt, |
| 2394 | 2497 | .cmp_lte, |
| 2498 | .cmp_gt, |
| 2499 | .cmp_gte, |
| 2395 | 2500 | => { |
| 2396 | 2501 | _ = try func.addInst(.{ |
| 2397 | 2502 | .tag = .pseudo, |
| 2398 | 2503 | .ops = .pseudo_compare, |
| 2399 | 2504 | .data = .{ |
| 2400 | 2505 | .compare = .{ |
| 2401 | | .rd = dest_reg, |
| 2402 | | .rs1 = lhs_reg, |
| 2403 | | .rs2 = rhs_reg, |
| 2404 | 2506 | .op = switch (tag) { |
| 2405 | 2507 | .cmp_eq => .eq, |
| 2406 | 2508 | .cmp_neq => .neq, |
| 2407 | | .cmp_gt => .gt, |
| 2408 | | .cmp_gte => .gte, |
| 2409 | 2509 | .cmp_lt => .lt, |
| 2410 | 2510 | .cmp_lte => .lte, |
| 2511 | .cmp_gt => .gt, |
| 2512 | .cmp_gte => .gte, |
| 2411 | 2513 | else => unreachable, |
| 2412 | 2514 | }, |
| 2413 | | .size = func.memSize(lhs_ty), |
| 2515 | .rd = dst_reg, |
| 2516 | .rs1 = lhs_reg, |
| 2517 | .rs2 = rhs_reg, |
| 2518 | .ty = lhs_ty, |
| 2414 | 2519 | }, |
| 2415 | 2520 | }, |
| 2416 | 2521 | }); |
| 2417 | 2522 | }, |
| 2418 | 2523 | |
| 2419 | | else => unreachable, |
| 2420 | | } |
| 2421 | | |
| 2422 | | return MCValue{ .register = dest_reg }; |
| 2423 | | } |
| 2424 | | |
| 2425 | | fn airPtrArithmetic(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 2426 | | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2427 | | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2428 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2429 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2430 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2431 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2432 | | |
| 2433 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2434 | | break :result try func.binOp(tag, lhs, lhs_ty, rhs, rhs_ty); |
| 2435 | | }; |
| 2436 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2437 | | } |
| 2438 | | |
| 2439 | | fn airAddWrap(func: *Func, inst: Air.Inst.Index) !void { |
| 2440 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2441 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement addwrap for {}", .{func.target.cpu.arch}); |
| 2442 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2443 | | } |
| 2444 | | |
| 2445 | | fn airAddSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2446 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2447 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement add_sat for {}", .{func.target.cpu.arch}); |
| 2448 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2449 | | } |
| 2450 | | |
| 2451 | | fn airSubWrap(func: *Func, inst: Air.Inst.Index) !void { |
| 2452 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2453 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2454 | | // RISCV arthemtic instructions already wrap, so this is simply a sub binOp with |
| 2455 | | // no overflow checks. |
| 2456 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2457 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2458 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2459 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2460 | | |
| 2461 | | break :result try func.binOp(.sub, lhs, lhs_ty, rhs, rhs_ty); |
| 2462 | | }; |
| 2463 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2464 | | } |
| 2465 | | |
| 2466 | | fn airSubSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2467 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2468 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement sub_sat for {}", .{func.target.cpu.arch}); |
| 2469 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2470 | | } |
| 2524 | // A branchless @min/@max sequence. |
| 2525 | // |
| 2526 | // Assume that a0 and a1 are the lhs and rhs respectively. |
| 2527 | // Also assume that a2 is the destination register. |
| 2528 | // |
| 2529 | // Algorithm: |
| 2530 | // slt s0, a0, a1 |
| 2531 | // sub s0, zero, s0 |
| 2532 | // xor a2, a0, a1 |
| 2533 | // and s0, a2, s0 |
| 2534 | // xor a2, a0, s0 # a0 is @min, a1 is @max |
| 2535 | // |
| 2536 | // "slt s0, a0, a1" will set s0 to 1 if a0 is less than a1, and 1 otherwise. |
| 2537 | // |
| 2538 | // "sub s0, zero, s0" will set all the bits of s0 to 1 if it was 1, otherwise it'll remain at 0. |
| 2539 | // |
| 2540 | // "xor a2, a0, a1" stores the bitwise XOR of a0 and a1 in a2. Effectively getting the difference between them. |
| 2541 | // |
| 2542 | // "and a0, a2, s0" here we mask the result of the XOR with the negated s0. If a0 < a1, s0 is -1, which |
| 2543 | // doesn't change the bits of a2. If a0 >= a1, s0 is 0, nullifying a2. |
| 2544 | // |
| 2545 | // "xor a2, a0, s0" the final XOR operation adjusts a2 to be the minimum value of a0 and a1. If a0 was less than |
| 2546 | // a1, s0 was -1, flipping all the bits in a2 and effectively restoring a0. If a0 was greater than or equal to a1, |
| 2547 | // s0 was 0, leaving a2 unchanged as a0. |
| 2548 | .min, .max => { |
| 2549 | const int_info = lhs_ty.intInfo(zcu); |
| 2550 | |
| 2551 | const mask_reg, const mask_lock = try func.allocReg(.int); |
| 2552 | defer func.register_manager.unlockReg(mask_lock); |
| 2471 | 2553 | |
| 2472 | | fn airMul(func: *Func, inst: Air.Inst.Index) !void { |
| 2473 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2474 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2475 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2476 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2477 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2478 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2479 | | |
| 2480 | | break :result try func.binOp(.mul, lhs, lhs_ty, rhs, rhs_ty); |
| 2481 | | }; |
| 2482 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2483 | | } |
| 2554 | _ = try func.addInst(.{ |
| 2555 | .tag = if (int_info.signedness == .unsigned) .sltu else .slt, |
| 2556 | .ops = .rrr, |
| 2557 | .data = .{ .r_type = .{ |
| 2558 | .rd = mask_reg, |
| 2559 | .rs1 = lhs_reg, |
| 2560 | .rs2 = rhs_reg, |
| 2561 | } }, |
| 2562 | }); |
| 2484 | 2563 | |
| 2485 | | fn airDiv(func: *Func, inst: Air.Inst.Index) !void { |
| 2486 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2487 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2488 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2489 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2490 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2491 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2564 | _ = try func.addInst(.{ |
| 2565 | .tag = .sub, |
| 2566 | .ops = .rrr, |
| 2567 | .data = .{ .r_type = .{ |
| 2568 | .rd = mask_reg, |
| 2569 | .rs1 = .zero, |
| 2570 | .rs2 = mask_reg, |
| 2571 | } }, |
| 2572 | }); |
| 2492 | 2573 | |
| 2493 | | break :result try func.binOp(.div_float, lhs, lhs_ty, rhs, rhs_ty); |
| 2494 | | }; |
| 2495 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2496 | | } |
| 2574 | _ = try func.addInst(.{ |
| 2575 | .tag = .xor, |
| 2576 | .ops = .rrr, |
| 2577 | .data = .{ .r_type = .{ |
| 2578 | .rd = dst_reg, |
| 2579 | .rs1 = lhs_reg, |
| 2580 | .rs2 = rhs_reg, |
| 2581 | } }, |
| 2582 | }); |
| 2497 | 2583 | |
| 2498 | | fn airMulWrap(func: *Func, inst: Air.Inst.Index) !void { |
| 2499 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2500 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement mulwrap for {}", .{func.target.cpu.arch}); |
| 2501 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2584 | _ = try func.addInst(.{ |
| 2585 | .tag = .@"and", |
| 2586 | .ops = .rrr, |
| 2587 | .data = .{ .r_type = .{ |
| 2588 | .rd = mask_reg, |
| 2589 | .rs1 = dst_reg, |
| 2590 | .rs2 = mask_reg, |
| 2591 | } }, |
| 2592 | }); |
| 2593 | |
| 2594 | _ = try func.addInst(.{ |
| 2595 | .tag = .xor, |
| 2596 | .ops = .rrr, |
| 2597 | .data = .{ .r_type = .{ |
| 2598 | .rd = dst_reg, |
| 2599 | .rs1 = if (tag == .min) rhs_reg else lhs_reg, |
| 2600 | .rs2 = mask_reg, |
| 2601 | } }, |
| 2602 | }); |
| 2603 | }, |
| 2604 | else => return func.fail("TODO: genBinOp {}", .{tag}), |
| 2605 | } |
| 2502 | 2606 | } |
| 2503 | 2607 | |
| 2504 | | fn airMulSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2505 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2506 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement mul_sat for {}", .{func.target.cpu.arch}); |
| 2507 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2608 | fn airPtrArithmetic(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 2609 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2610 | const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2611 | const dst_mcv = try func.binOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 2612 | return func.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2508 | 2613 | } |
| 2509 | 2614 | |
| 2510 | 2615 | fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2513,19 +2618,16 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2513 | 2618 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2514 | 2619 | |
| 2515 | 2620 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2516 | | const lhs = try func.resolveInst(extra.lhs); |
| 2517 | | const rhs = try func.resolveInst(extra.rhs); |
| 2518 | 2621 | const lhs_ty = func.typeOf(extra.lhs); |
| 2519 | | const rhs_ty = func.typeOf(extra.rhs); |
| 2520 | 2622 | |
| 2521 | 2623 | const int_info = lhs_ty.intInfo(zcu); |
| 2522 | 2624 | |
| 2523 | 2625 | const tuple_ty = func.typeOfIndex(inst); |
| 2524 | | const result_mcv = try func.allocRegOrMem(inst, false); |
| 2626 | const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); |
| 2525 | 2627 | const offset = result_mcv.load_frame; |
| 2526 | 2628 | |
| 2527 | 2629 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 2528 | | const add_result = try func.binOp(.add, lhs, lhs_ty, rhs, rhs_ty); |
| 2630 | const add_result = try func.binOp(null, .add, extra.lhs, extra.rhs); |
| 2529 | 2631 | const add_result_reg = try func.copyToTmpRegister(lhs_ty, add_result); |
| 2530 | 2632 | const add_result_reg_lock = func.register_manager.lockRegAssumeUnused(add_result_reg); |
| 2531 | 2633 | defer func.register_manager.unlockReg(add_result_reg_lock); |
| ... | ... | @@ -2542,7 +2644,7 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2542 | 2644 | .i_type = .{ |
| 2543 | 2645 | .rd = shift_reg, |
| 2544 | 2646 | .rs1 = add_result_reg, |
| 2545 | | .imm12 = Immediate.s(shift_amount), |
| 2647 | .imm12 = Immediate.u(shift_amount), |
| 2546 | 2648 | }, |
| 2547 | 2649 | }, |
| 2548 | 2650 | }); |
| ... | ... | @@ -2554,7 +2656,7 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2554 | 2656 | .i_type = .{ |
| 2555 | 2657 | .rd = shift_reg, |
| 2556 | 2658 | .rs1 = shift_reg, |
| 2557 | | .imm12 = Immediate.s(shift_amount), |
| 2659 | .imm12 = Immediate.u(shift_amount), |
| 2558 | 2660 | }, |
| 2559 | 2661 | }, |
| 2560 | 2662 | }); |
| ... | ... | @@ -2566,18 +2668,23 @@ fn airAddWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2566 | 2668 | add_result, |
| 2567 | 2669 | ); |
| 2568 | 2670 | |
| 2569 | | const overflow_mcv = try func.binOp( |
| 2671 | const overflow_reg, const overflow_lock = try func.allocReg(.int); |
| 2672 | defer func.register_manager.unlockReg(overflow_lock); |
| 2673 | |
| 2674 | try func.genBinOp( |
| 2570 | 2675 | .cmp_neq, |
| 2571 | 2676 | .{ .register = shift_reg }, |
| 2572 | 2677 | lhs_ty, |
| 2573 | 2678 | .{ .register = add_result_reg }, |
| 2574 | 2679 | lhs_ty, |
| 2680 | overflow_reg, |
| 2575 | 2681 | ); |
| 2682 | |
| 2576 | 2683 | try func.genSetMem( |
| 2577 | 2684 | .{ .frame = offset.index }, |
| 2578 | 2685 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))), |
| 2579 | 2686 | Type.u1, |
| 2580 | | overflow_mcv, |
| 2687 | .{ .register = overflow_reg }, |
| 2581 | 2688 | ); |
| 2582 | 2689 | |
| 2583 | 2690 | break :result result_mcv; |
| ... | ... | @@ -2602,49 +2709,64 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2602 | 2709 | |
| 2603 | 2710 | const int_info = lhs_ty.intInfo(zcu); |
| 2604 | 2711 | |
| 2605 | | if (!math.isPowerOfTwo(int_info.bits) or !(int_info.bits >= 8)) { |
| 2712 | if (!math.isPowerOfTwo(int_info.bits) or int_info.bits < 8) { |
| 2606 | 2713 | return func.fail("TODO: airSubWithOverflow non-power of 2 and less than 8 bits", .{}); |
| 2607 | 2714 | } |
| 2608 | 2715 | |
| 2716 | if (int_info.bits > 64) { |
| 2717 | return func.fail("TODO: airSubWithOverflow > 64 bits", .{}); |
| 2718 | } |
| 2719 | |
| 2609 | 2720 | const tuple_ty = func.typeOfIndex(inst); |
| 2610 | | const result_mcv = try func.allocRegOrMem(inst, false); |
| 2721 | const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); |
| 2611 | 2722 | const offset = result_mcv.load_frame; |
| 2612 | 2723 | |
| 2613 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 2724 | const dest_mcv = try func.binOp(null, .sub, extra.lhs, extra.rhs); |
| 2725 | assert(dest_mcv == .register); |
| 2726 | const dest_reg = dest_mcv.register; |
| 2727 | |
| 2728 | try func.genSetMem( |
| 2729 | .{ .frame = offset.index }, |
| 2730 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))), |
| 2731 | lhs_ty, |
| 2732 | .{ .register = dest_reg }, |
| 2733 | ); |
| 2734 | |
| 2735 | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs); |
| 2614 | 2736 | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2615 | 2737 | |
| 2616 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{}); |
| 2738 | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs); |
| 2617 | 2739 | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2618 | 2740 | |
| 2619 | | const dest_reg, const dest_lock = try func.allocReg(.int); |
| 2620 | | defer func.register_manager.unlockReg(dest_lock); |
| 2741 | const overflow_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = 0 }); |
| 2742 | |
| 2743 | const overflow_lock = func.register_manager.lockRegAssumeUnused(overflow_reg); |
| 2744 | defer func.register_manager.unlockReg(overflow_lock); |
| 2621 | 2745 | |
| 2622 | 2746 | switch (int_info.signedness) { |
| 2623 | | .unsigned => return func.fail("TODO: airSubWithOverflow unsigned", .{}), |
| 2747 | .unsigned => { |
| 2748 | _ = try func.addInst(.{ |
| 2749 | .tag = .sltu, |
| 2750 | .ops = .rrr, |
| 2751 | .data = .{ .r_type = .{ |
| 2752 | .rd = overflow_reg, |
| 2753 | .rs1 = lhs_reg, |
| 2754 | .rs2 = rhs_reg, |
| 2755 | } }, |
| 2756 | }); |
| 2757 | |
| 2758 | try func.genSetMem( |
| 2759 | .{ .frame = offset.index }, |
| 2760 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))), |
| 2761 | Type.u1, |
| 2762 | .{ .register = overflow_reg }, |
| 2763 | ); |
| 2764 | |
| 2765 | break :result result_mcv; |
| 2766 | }, |
| 2624 | 2767 | .signed => { |
| 2625 | 2768 | switch (int_info.bits) { |
| 2626 | 2769 | 64 => { |
| 2627 | | // result |
| 2628 | | _ = try func.addInst(.{ |
| 2629 | | .tag = .sub, |
| 2630 | | .ops = .rrr, |
| 2631 | | .data = .{ .r_type = .{ |
| 2632 | | .rd = dest_reg, |
| 2633 | | .rs1 = lhs_reg, |
| 2634 | | .rs2 = rhs_reg, |
| 2635 | | } }, |
| 2636 | | }); |
| 2637 | | |
| 2638 | | try func.genSetMem( |
| 2639 | | .{ .frame = offset.index }, |
| 2640 | | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))), |
| 2641 | | lhs_ty, |
| 2642 | | .{ .register = dest_reg }, |
| 2643 | | ); |
| 2644 | | |
| 2645 | | // overflow check |
| 2646 | | const overflow_reg = try func.copyToTmpRegister(Type.usize, .{ .immediate = 0 }); |
| 2647 | | |
| 2648 | 2770 | _ = try func.addInst(.{ |
| 2649 | 2771 | .tag = .slt, |
| 2650 | 2772 | .ops = .rrr, |
| ... | ... | @@ -2675,19 +2797,20 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2675 | 2797 | } }, |
| 2676 | 2798 | }); |
| 2677 | 2799 | |
| 2678 | | const overflow_mcv = try func.binOp( |
| 2800 | try func.genBinOp( |
| 2679 | 2801 | .cmp_neq, |
| 2680 | 2802 | .{ .register = overflow_reg }, |
| 2681 | 2803 | Type.usize, |
| 2682 | 2804 | .{ .register = rhs_reg }, |
| 2683 | 2805 | Type.usize, |
| 2806 | overflow_reg, |
| 2684 | 2807 | ); |
| 2685 | 2808 | |
| 2686 | 2809 | try func.genSetMem( |
| 2687 | 2810 | .{ .frame = offset.index }, |
| 2688 | 2811 | offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))), |
| 2689 | 2812 | Type.u1, |
| 2690 | | overflow_mcv, |
| 2813 | .{ .register = overflow_reg }, |
| 2691 | 2814 | ); |
| 2692 | 2815 | |
| 2693 | 2816 | break :result result_mcv; |
| ... | ... | @@ -2702,16 +2825,42 @@ fn airSubWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2702 | 2825 | } |
| 2703 | 2826 | |
| 2704 | 2827 | fn airMulWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2705 | | //const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 2828 | const zcu = func.bin_file.comp.module.?; |
| 2706 | 2829 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2707 | 2830 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2708 | | const zcu = func.bin_file.comp.module.?; |
| 2831 | |
| 2709 | 2832 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2710 | 2833 | const lhs = try func.resolveInst(extra.lhs); |
| 2711 | 2834 | const rhs = try func.resolveInst(extra.rhs); |
| 2712 | 2835 | const lhs_ty = func.typeOf(extra.lhs); |
| 2713 | 2836 | const rhs_ty = func.typeOf(extra.rhs); |
| 2714 | 2837 | |
| 2838 | const tuple_ty = func.typeOfIndex(inst); |
| 2839 | |
| 2840 | // genSetReg needs to support register_offset src_mcv for this to be true. |
| 2841 | const result_mcv = try func.allocRegOrMem(tuple_ty, inst, false); |
| 2842 | |
| 2843 | const result_off: i32 = @intCast(tuple_ty.structFieldOffset(0, zcu)); |
| 2844 | const overflow_off: i32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); |
| 2845 | |
| 2846 | const dest_reg, const dest_lock = try func.allocReg(.int); |
| 2847 | defer func.register_manager.unlockReg(dest_lock); |
| 2848 | |
| 2849 | try func.genBinOp( |
| 2850 | .mul, |
| 2851 | lhs, |
| 2852 | lhs_ty, |
| 2853 | rhs, |
| 2854 | rhs_ty, |
| 2855 | dest_reg, |
| 2856 | ); |
| 2857 | |
| 2858 | try func.genCopy( |
| 2859 | lhs_ty, |
| 2860 | result_mcv.offset(result_off), |
| 2861 | .{ .register = dest_reg }, |
| 2862 | ); |
| 2863 | |
| 2715 | 2864 | switch (lhs_ty.zigTypeTag(zcu)) { |
| 2716 | 2865 | else => |x| return func.fail("TODO: airMulWithOverflow {s}", .{@tagName(x)}), |
| 2717 | 2866 | .Int => { |
| ... | ... | @@ -2719,74 +2868,53 @@ fn airMulWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2719 | 2868 | const int_info = lhs_ty.intInfo(zcu); |
| 2720 | 2869 | switch (int_info.bits) { |
| 2721 | 2870 | 1...32 => { |
| 2722 | | if (func.hasFeature(.m)) { |
| 2723 | | const dest = try func.binOp(.mul, lhs, lhs_ty, rhs, rhs_ty); |
| 2724 | | |
| 2725 | | const add_result_lock = func.register_manager.lockRegAssumeUnused(dest.register); |
| 2726 | | defer func.register_manager.unlockReg(add_result_lock); |
| 2727 | | |
| 2728 | | const tuple_ty = func.typeOfIndex(inst); |
| 2729 | | |
| 2730 | | const result_mcv = try func.allocRegOrMem(inst, true); |
| 2731 | | |
| 2732 | | const result_off: i32 = @intCast(tuple_ty.structFieldOffset(0, zcu)); |
| 2733 | | const overflow_off: i32 = @intCast(tuple_ty.structFieldOffset(1, zcu)); |
| 2734 | | |
| 2735 | | try func.genCopy( |
| 2736 | | lhs_ty, |
| 2737 | | result_mcv.offset(result_off), |
| 2738 | | dest, |
| 2739 | | ); |
| 2740 | | |
| 2741 | | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 2742 | | if (int_info.signedness == .unsigned) { |
| 2743 | | switch (int_info.bits) { |
| 2744 | | 1...8 => { |
| 2745 | | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; |
| 2746 | | |
| 2747 | | const add_reg, const add_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 2748 | | defer if (add_lock) |lock| func.register_manager.unlockReg(lock); |
| 2749 | | |
| 2750 | | const overflow_reg, const overflow_lock = try func.allocReg(.int); |
| 2751 | | defer func.register_manager.unlockReg(overflow_lock); |
| 2752 | | |
| 2753 | | _ = try func.addInst(.{ |
| 2754 | | .tag = .andi, |
| 2755 | | .ops = .rri, |
| 2756 | | .data = .{ .i_type = .{ |
| 2757 | | .rd = overflow_reg, |
| 2758 | | .rs1 = add_reg, |
| 2759 | | .imm12 = Immediate.s(max_val), |
| 2760 | | } }, |
| 2761 | | }); |
| 2762 | | |
| 2763 | | const overflow_mcv = try func.binOp( |
| 2764 | | .cmp_neq, |
| 2765 | | .{ .register = overflow_reg }, |
| 2766 | | lhs_ty, |
| 2767 | | .{ .register = add_reg }, |
| 2768 | | lhs_ty, |
| 2769 | | ); |
| 2770 | | |
| 2771 | | try func.genCopy( |
| 2772 | | lhs_ty, |
| 2773 | | result_mcv.offset(overflow_off), |
| 2774 | | overflow_mcv, |
| 2775 | | ); |
| 2776 | | |
| 2777 | | break :result result_mcv; |
| 2778 | | }, |
| 2779 | | |
| 2780 | | else => return func.fail("TODO: airMulWithOverflow check for size {d}", .{int_info.bits}), |
| 2781 | | } |
| 2782 | | } else { |
| 2783 | | return func.fail("TODO: airMulWithOverflow calculate carry for signed addition", .{}); |
| 2871 | if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) { |
| 2872 | if (int_info.signedness == .unsigned) { |
| 2873 | switch (int_info.bits) { |
| 2874 | 1...8 => { |
| 2875 | const max_val = std.math.pow(u16, 2, int_info.bits) - 1; |
| 2876 | |
| 2877 | const add_reg, const add_lock = try func.promoteReg(lhs_ty, lhs); |
| 2878 | defer if (add_lock) |lock| func.register_manager.unlockReg(lock); |
| 2879 | |
| 2880 | const overflow_reg, const overflow_lock = try func.allocReg(.int); |
| 2881 | defer func.register_manager.unlockReg(overflow_lock); |
| 2882 | |
| 2883 | _ = try func.addInst(.{ |
| 2884 | .tag = .andi, |
| 2885 | .ops = .rri, |
| 2886 | .data = .{ .i_type = .{ |
| 2887 | .rd = overflow_reg, |
| 2888 | .rs1 = add_reg, |
| 2889 | .imm12 = Immediate.s(max_val), |
| 2890 | } }, |
| 2891 | }); |
| 2892 | |
| 2893 | try func.genBinOp( |
| 2894 | .cmp_neq, |
| 2895 | .{ .register = overflow_reg }, |
| 2896 | lhs_ty, |
| 2897 | .{ .register = add_reg }, |
| 2898 | lhs_ty, |
| 2899 | overflow_reg, |
| 2900 | ); |
| 2901 | |
| 2902 | try func.genCopy( |
| 2903 | lhs_ty, |
| 2904 | result_mcv.offset(overflow_off), |
| 2905 | .{ .register = overflow_reg }, |
| 2906 | ); |
| 2907 | |
| 2908 | break :result result_mcv; |
| 2909 | }, |
| 2910 | |
| 2911 | else => return func.fail("TODO: airMulWithOverflow check for size {d}", .{int_info.bits}), |
| 2784 | 2912 | } |
| 2785 | 2913 | } else { |
| 2786 | | return func.fail("TODO: airMulWithOverflow with < 8 bits or non-pow of 2", .{}); |
| 2914 | return func.fail("TODO: airMulWithOverflow calculate carry for signed addition", .{}); |
| 2787 | 2915 | } |
| 2788 | 2916 | } else { |
| 2789 | | return func.fail("TODO: emulate mul for targets without M feature", .{}); |
| 2917 | return func.fail("TODO: airMulWithOverflow with < 8 bits or non-pow of 2", .{}); |
| 2790 | 2918 | } |
| 2791 | 2919 | }, |
| 2792 | 2920 | else => return func.fail("TODO: airMulWithOverflow larger than 32-bit mul", .{}), |
| ... | ... | @@ -2799,123 +2927,32 @@ fn airMulWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2799 | 2927 | } |
| 2800 | 2928 | |
| 2801 | 2929 | fn airShlWithOverflow(func: *Func, inst: Air.Inst.Index) !void { |
| 2802 | | _ = inst; |
| 2803 | | return func.fail("TODO implement airShlWithOverflow for {}", .{func.target.cpu.arch}); |
| 2804 | | } |
| 2805 | | |
| 2806 | | fn airRem(func: *Func, inst: Air.Inst.Index) !void { |
| 2807 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2808 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement rem for {}", .{func.target.cpu.arch}); |
| 2809 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2810 | | } |
| 2811 | | |
| 2812 | | fn airMod(func: *Func, inst: Air.Inst.Index) !void { |
| 2813 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2814 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement zcu for {}", .{func.target.cpu.arch}); |
| 2815 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2816 | | } |
| 2817 | | |
| 2818 | | fn airBitAnd(func: *Func, inst: Air.Inst.Index) !void { |
| 2819 | 2930 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2820 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2821 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2822 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2823 | | |
| 2824 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2825 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2826 | | |
| 2827 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 2828 | | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2829 | | |
| 2830 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{}); |
| 2831 | | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2832 | | |
| 2833 | | const dest_reg, const dest_lock = try func.allocReg(.int); |
| 2834 | | defer func.register_manager.unlockReg(dest_lock); |
| 2835 | | |
| 2836 | | _ = try func.addInst(.{ |
| 2837 | | .tag = .@"and", |
| 2838 | | .ops = .rrr, |
| 2839 | | .data = .{ .r_type = .{ |
| 2840 | | .rd = dest_reg, |
| 2841 | | .rs1 = lhs_reg, |
| 2842 | | .rs2 = rhs_reg, |
| 2843 | | } }, |
| 2844 | | }); |
| 2845 | | |
| 2846 | | break :result .{ .register = dest_reg }; |
| 2847 | | }; |
| 2931 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airShlWithOverflow", .{}); |
| 2848 | 2932 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2849 | 2933 | } |
| 2850 | 2934 | |
| 2851 | | fn airBitOr(func: *Func, inst: Air.Inst.Index) !void { |
| 2935 | fn airAddSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2852 | 2936 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2853 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2854 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2855 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2856 | | |
| 2857 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2858 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2859 | | |
| 2860 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 2861 | | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2862 | | |
| 2863 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{}); |
| 2864 | | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 2865 | | |
| 2866 | | const dest_reg, const dest_lock = try func.allocReg(.int); |
| 2867 | | defer func.register_manager.unlockReg(dest_lock); |
| 2868 | | |
| 2869 | | _ = try func.addInst(.{ |
| 2870 | | .tag = .@"or", |
| 2871 | | .ops = .rrr, |
| 2872 | | .data = .{ .r_type = .{ |
| 2873 | | .rd = dest_reg, |
| 2874 | | .rs1 = lhs_reg, |
| 2875 | | .rs2 = rhs_reg, |
| 2876 | | } }, |
| 2877 | | }); |
| 2878 | | |
| 2879 | | break :result .{ .register = dest_reg }; |
| 2880 | | }; |
| 2937 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airAddSat", .{}); |
| 2881 | 2938 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2882 | 2939 | } |
| 2883 | 2940 | |
| 2884 | | fn airXor(func: *Func, inst: Air.Inst.Index) !void { |
| 2941 | fn airSubSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2885 | 2942 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2886 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement xor for {}", .{func.target.cpu.arch}); |
| 2943 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airSubSat", .{}); |
| 2887 | 2944 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2888 | 2945 | } |
| 2889 | 2946 | |
| 2890 | | fn airShl(func: *Func, inst: Air.Inst.Index) !void { |
| 2947 | fn airMulSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2891 | 2948 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2892 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2893 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2894 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2895 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2896 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2897 | | |
| 2898 | | break :result try func.binOp(.shl, lhs, lhs_ty, rhs, rhs_ty); |
| 2899 | | }; |
| 2949 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airMulSat", .{}); |
| 2900 | 2950 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2901 | 2951 | } |
| 2902 | 2952 | |
| 2903 | 2953 | fn airShlSat(func: *Func, inst: Air.Inst.Index) !void { |
| 2904 | 2954 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2905 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement shl_sat for {}", .{func.target.cpu.arch}); |
| 2906 | | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2907 | | } |
| 2908 | | |
| 2909 | | fn airShr(func: *Func, inst: Air.Inst.Index) !void { |
| 2910 | | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 2911 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 2912 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 2913 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 2914 | | const lhs_ty = func.typeOf(bin_op.lhs); |
| 2915 | | const rhs_ty = func.typeOf(bin_op.rhs); |
| 2916 | | |
| 2917 | | break :result try func.binOp(.shr, lhs, lhs_ty, rhs, rhs_ty); |
| 2918 | | }; |
| 2955 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement airShlSat", .{}); |
| 2919 | 2956 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2920 | 2957 | } |
| 2921 | 2958 | |
| ... | ... | @@ -2935,7 +2972,7 @@ fn airOptionalPayload(func: *Func, inst: Air.Inst.Index) !void { |
| 2935 | 2972 | break :result opt_mcv; |
| 2936 | 2973 | } |
| 2937 | 2974 | |
| 2938 | | const pl_mcv = try func.allocRegOrMem(inst, true); |
| 2975 | const pl_mcv = try func.allocRegOrMem(pl_ty, inst, true); |
| 2939 | 2976 | try func.genCopy(pl_ty, pl_mcv, opt_mcv); |
| 2940 | 2977 | break :result pl_mcv; |
| 2941 | 2978 | }; |
| ... | ... | @@ -2978,15 +3015,15 @@ fn airUnwrapErrErr(func: *Func, inst: Air.Inst.Index) !void { |
| 2978 | 3015 | const eu_lock = func.register_manager.lockReg(reg); |
| 2979 | 3016 | defer if (eu_lock) |lock| func.register_manager.unlockReg(lock); |
| 2980 | 3017 | |
| 2981 | | var result = try func.copyToNewRegister(inst, operand); |
| 2982 | | |
| 3018 | const result = try func.copyToNewRegister(inst, operand); |
| 2983 | 3019 | if (err_off > 0) { |
| 2984 | | result = try func.binOp( |
| 3020 | try func.genBinOp( |
| 2985 | 3021 | .shr, |
| 2986 | 3022 | result, |
| 2987 | 3023 | err_union_ty, |
| 2988 | 3024 | .{ .immediate = @as(u6, @intCast(err_off * 8)) }, |
| 2989 | 3025 | Type.u8, |
| 3026 | result.register, |
| 2990 | 3027 | ); |
| 2991 | 3028 | } |
| 2992 | 3029 | break :result result; |
| ... | ... | @@ -3031,19 +3068,18 @@ fn genUnwrapErrUnionPayloadMir( |
| 3031 | 3068 | const eu_lock = func.register_manager.lockReg(reg); |
| 3032 | 3069 | defer if (eu_lock) |lock| func.register_manager.unlockReg(lock); |
| 3033 | 3070 | |
| 3034 | | var result: MCValue = .{ .register = try func.copyToTmpRegister(err_union_ty, err_union) }; |
| 3035 | | |
| 3071 | const result_reg = try func.copyToTmpRegister(err_union_ty, err_union); |
| 3036 | 3072 | if (payload_off > 0) { |
| 3037 | | result = try func.binOp( |
| 3073 | try func.genBinOp( |
| 3038 | 3074 | .shr, |
| 3039 | | result, |
| 3075 | .{ .register = result_reg }, |
| 3040 | 3076 | err_union_ty, |
| 3041 | 3077 | .{ .immediate = @as(u6, @intCast(payload_off * 8)) }, |
| 3042 | 3078 | Type.u8, |
| 3079 | result_reg, |
| 3043 | 3080 | ); |
| 3044 | 3081 | } |
| 3045 | | |
| 3046 | | break :result result; |
| 3082 | break :result .{ .register = result_reg }; |
| 3047 | 3083 | }, |
| 3048 | 3084 | else => return func.fail("TODO implement genUnwrapErrUnionPayloadMir for {}", .{err_union}), |
| 3049 | 3085 | } |
| ... | ... | @@ -3108,7 +3144,7 @@ fn airWrapOptional(func: *Func, inst: Air.Inst.Index) !void { |
| 3108 | 3144 | }; |
| 3109 | 3145 | defer if (pl_lock) |lock| func.register_manager.unlockReg(lock); |
| 3110 | 3146 | |
| 3111 | | const opt_mcv = try func.allocRegOrMem(inst, true); |
| 3147 | const opt_mcv = try func.allocRegOrMem(opt_ty, inst, true); |
| 3112 | 3148 | try func.genCopy(pl_ty, opt_mcv, pl_mcv); |
| 3113 | 3149 | |
| 3114 | 3150 | if (!same_repr) { |
| ... | ... | @@ -3237,7 +3273,7 @@ fn airSlicePtr(func: *Func, inst: Air.Inst.Index) !void { |
| 3237 | 3273 | const src_mcv = try func.resolveInst(ty_op.operand); |
| 3238 | 3274 | if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; |
| 3239 | 3275 | |
| 3240 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 3276 | const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 3241 | 3277 | const dst_ty = func.typeOfIndex(inst); |
| 3242 | 3278 | try func.genCopy(dst_ty, dst_mcv, src_mcv); |
| 3243 | 3279 | break :result dst_mcv; |
| ... | ... | @@ -3249,6 +3285,8 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { |
| 3249 | 3285 | const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 3250 | 3286 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 3251 | 3287 | const src_mcv = try func.resolveInst(ty_op.operand); |
| 3288 | const ty = func.typeOfIndex(inst); |
| 3289 | |
| 3252 | 3290 | switch (src_mcv) { |
| 3253 | 3291 | .load_frame => |frame_addr| { |
| 3254 | 3292 | const len_mcv: MCValue = .{ .load_frame = .{ |
| ... | ... | @@ -3257,7 +3295,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { |
| 3257 | 3295 | } }; |
| 3258 | 3296 | if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; |
| 3259 | 3297 | |
| 3260 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 3298 | const dst_mcv = try func.allocRegOrMem(ty, inst, true); |
| 3261 | 3299 | try func.genCopy(Type.usize, dst_mcv, len_mcv); |
| 3262 | 3300 | break :result dst_mcv; |
| 3263 | 3301 | }, |
| ... | ... | @@ -3266,7 +3304,7 @@ fn airSliceLen(func: *Func, inst: Air.Inst.Index) !void { |
| 3266 | 3304 | |
| 3267 | 3305 | if (func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; |
| 3268 | 3306 | |
| 3269 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 3307 | const dst_mcv = try func.allocRegOrMem(ty, inst, true); |
| 3270 | 3308 | try func.genCopy(Type.usize, dst_mcv, len_mcv); |
| 3271 | 3309 | break :result dst_mcv; |
| 3272 | 3310 | }, |
| ... | ... | @@ -3289,41 +3327,19 @@ fn airPtrSlicePtrPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 3289 | 3327 | } |
| 3290 | 3328 | |
| 3291 | 3329 | fn airSliceElemVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3292 | | const zcu = func.bin_file.comp.module.?; |
| 3293 | | const is_volatile = false; // TODO |
| 3330 | const mod = func.bin_file.comp.module.?; |
| 3294 | 3331 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3295 | 3332 | |
| 3296 | | if (!is_volatile and func.liveness.isUnused(inst)) return func.finishAir( |
| 3297 | | inst, |
| 3298 | | .unreach, |
| 3299 | | .{ bin_op.lhs, bin_op.rhs, .none }, |
| 3300 | | ); |
| 3301 | 3333 | const result: MCValue = result: { |
| 3302 | | const slice_mcv = try func.resolveInst(bin_op.lhs); |
| 3303 | | const index_mcv = try func.resolveInst(bin_op.rhs); |
| 3334 | const elem_ty = func.typeOfIndex(inst); |
| 3335 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) break :result .none; |
| 3304 | 3336 | |
| 3305 | 3337 | const slice_ty = func.typeOf(bin_op.lhs); |
| 3306 | | |
| 3307 | | const slice_ptr_field_type = slice_ty.slicePtrFieldType(zcu); |
| 3308 | | |
| 3309 | | const index_lock: ?RegisterLock = if (index_mcv == .register) |
| 3310 | | func.register_manager.lockRegAssumeUnused(index_mcv.register) |
| 3311 | | else |
| 3312 | | null; |
| 3313 | | defer if (index_lock) |reg| func.register_manager.unlockReg(reg); |
| 3314 | | |
| 3315 | | const base_mcv: MCValue = switch (slice_mcv) { |
| 3316 | | .load_frame, |
| 3317 | | .load_symbol, |
| 3318 | | => .{ .register = try func.copyToTmpRegister(slice_ptr_field_type, slice_mcv) }, |
| 3319 | | else => return func.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), |
| 3320 | | }; |
| 3321 | | |
| 3322 | | const dest = try func.allocRegOrMem(inst, true); |
| 3323 | | const addr = try func.binOp(.ptr_add, base_mcv, slice_ptr_field_type, index_mcv, Type.usize); |
| 3324 | | try func.load(dest, addr, slice_ptr_field_type); |
| 3325 | | |
| 3326 | | break :result dest; |
| 3338 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(mod); |
| 3339 | const elem_ptr = try func.genSliceElemPtr(bin_op.lhs, bin_op.rhs); |
| 3340 | const dst_mcv = try func.allocRegOrMem(elem_ty, inst, false); |
| 3341 | try func.load(dst_mcv, elem_ptr, slice_ptr_field_type); |
| 3342 | break :result dst_mcv; |
| 3327 | 3343 | }; |
| 3328 | 3344 | return func.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 3329 | 3345 | } |
| ... | ... | @@ -3331,14 +3347,58 @@ fn airSliceElemVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3331 | 3347 | fn airSliceElemPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 3332 | 3348 | const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 3333 | 3349 | const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3334 | | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else return func.fail("TODO implement slice_elem_ptr for {}", .{func.target.cpu.arch}); |
| 3335 | | return func.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 3350 | const dst_mcv = try func.genSliceElemPtr(extra.lhs, extra.rhs); |
| 3351 | return func.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); |
| 3352 | } |
| 3353 | |
| 3354 | fn genSliceElemPtr(func: *Func, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 3355 | const zcu = func.bin_file.comp.module.?; |
| 3356 | const slice_ty = func.typeOf(lhs); |
| 3357 | const slice_mcv = try func.resolveInst(lhs); |
| 3358 | const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) { |
| 3359 | .register => |reg| func.register_manager.lockRegAssumeUnused(reg), |
| 3360 | else => null, |
| 3361 | }; |
| 3362 | defer if (slice_mcv_lock) |lock| func.register_manager.unlockReg(lock); |
| 3363 | |
| 3364 | const elem_ty = slice_ty.childType(zcu); |
| 3365 | const elem_size = elem_ty.abiSize(zcu); |
| 3366 | |
| 3367 | const index_ty = func.typeOf(rhs); |
| 3368 | const index_mcv = try func.resolveInst(rhs); |
| 3369 | const index_mcv_lock: ?RegisterLock = switch (index_mcv) { |
| 3370 | .register => |reg| func.register_manager.lockRegAssumeUnused(reg), |
| 3371 | else => null, |
| 3372 | }; |
| 3373 | defer if (index_mcv_lock) |lock| func.register_manager.unlockReg(lock); |
| 3374 | |
| 3375 | const offset_reg = try func.elemOffset(index_ty, index_mcv, elem_size); |
| 3376 | const offset_reg_lock = func.register_manager.lockRegAssumeUnused(offset_reg); |
| 3377 | defer func.register_manager.unlockReg(offset_reg_lock); |
| 3378 | |
| 3379 | const addr_reg, const addr_lock = try func.allocReg(.int); |
| 3380 | defer func.register_manager.unlockReg(addr_lock); |
| 3381 | try func.genSetReg(Type.usize, addr_reg, slice_mcv); |
| 3382 | |
| 3383 | _ = try func.addInst(.{ |
| 3384 | .tag = .add, |
| 3385 | .ops = .rrr, |
| 3386 | .data = .{ .r_type = .{ |
| 3387 | .rd = addr_reg, |
| 3388 | .rs1 = addr_reg, |
| 3389 | .rs2 = offset_reg, |
| 3390 | } }, |
| 3391 | }); |
| 3392 | |
| 3393 | return .{ .register = addr_reg }; |
| 3336 | 3394 | } |
| 3337 | 3395 | |
| 3338 | 3396 | fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3339 | 3397 | const zcu = func.bin_file.comp.module.?; |
| 3340 | 3398 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 3341 | 3399 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 3400 | const result_ty = func.typeOfIndex(inst); |
| 3401 | |
| 3342 | 3402 | const array_ty = func.typeOf(bin_op.lhs); |
| 3343 | 3403 | const array_mcv = try func.resolveInst(bin_op.lhs); |
| 3344 | 3404 | |
| ... | ... | @@ -3367,7 +3427,7 @@ fn airArrayElemVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3367 | 3427 | const offset_lock = func.register_manager.lockRegAssumeUnused(offset_reg); |
| 3368 | 3428 | defer func.register_manager.unlockReg(offset_lock); |
| 3369 | 3429 | |
| 3370 | | const dst_mcv = try func.allocRegOrMem(inst, false); |
| 3430 | const dst_mcv = try func.allocRegOrMem(result_ty, inst, false); |
| 3371 | 3431 | _ = try func.addInst(.{ |
| 3372 | 3432 | .tag = .add, |
| 3373 | 3433 | .ops = .rrr, |
| ... | ... | @@ -3427,7 +3487,19 @@ fn airPtrElemPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 3427 | 3487 | const offset_reg_lock = func.register_manager.lockRegAssumeUnused(offset_reg); |
| 3428 | 3488 | defer func.register_manager.unlockReg(offset_reg_lock); |
| 3429 | 3489 | |
| 3430 | | break :result try func.binOp(.ptr_add, base_ptr_mcv, base_ptr_ty, .{ .register = offset_reg }, base_ptr_ty); |
| 3490 | const result_reg, const result_lock = try func.allocReg(.int); |
| 3491 | defer func.register_manager.unlockReg(result_lock); |
| 3492 | |
| 3493 | try func.genBinOp( |
| 3494 | .ptr_add, |
| 3495 | base_ptr_mcv, |
| 3496 | base_ptr_ty, |
| 3497 | .{ .register = offset_reg }, |
| 3498 | Type.usize, |
| 3499 | result_reg, |
| 3500 | ); |
| 3501 | |
| 3502 | break :result MCValue{ .register = result_reg }; |
| 3431 | 3503 | }; |
| 3432 | 3504 | return func.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 3433 | 3505 | } |
| ... | ... | @@ -3487,7 +3559,7 @@ fn airAbs(func: *Func, inst: Air.Inst.Index) !void { |
| 3487 | 3559 | .data = .{ .i_type = .{ |
| 3488 | 3560 | .rd = temp_reg, |
| 3489 | 3561 | .rs1 = operand_reg, |
| 3490 | | .imm12 = Immediate.s(63), |
| 3562 | .imm12 = Immediate.u(63), |
| 3491 | 3563 | } }, |
| 3492 | 3564 | }); |
| 3493 | 3565 | |
| ... | ... | @@ -3695,7 +3767,7 @@ fn airLoad(func: *Func, inst: Air.Inst.Index) !void { |
| 3695 | 3767 | // The MCValue that holds the pointer can be re-used as the value. |
| 3696 | 3768 | break :blk ptr; |
| 3697 | 3769 | } else { |
| 3698 | | break :blk try func.allocRegOrMem(inst, true); |
| 3770 | break :blk try func.allocRegOrMem(elem_ty, inst, true); |
| 3699 | 3771 | } |
| 3700 | 3772 | }; |
| 3701 | 3773 | |
| ... | ... | @@ -3876,7 +3948,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3876 | 3948 | .tag = .srli, |
| 3877 | 3949 | .ops = .rri, |
| 3878 | 3950 | .data = .{ .i_type = .{ |
| 3879 | | .imm12 = Immediate.s(@intCast(field_off)), |
| 3951 | .imm12 = Immediate.u(@intCast(field_off)), |
| 3880 | 3952 | .rd = dst_reg, |
| 3881 | 3953 | .rs1 = dst_reg, |
| 3882 | 3954 | } }, |
| ... | ... | @@ -3911,7 +3983,7 @@ fn airStructFieldVal(func: *Func, inst: Air.Inst.Index) !void { |
| 3911 | 3983 | func.reuseOperand(inst, operand, 0, src_mcv)) |
| 3912 | 3984 | off_mcv |
| 3913 | 3985 | else dst: { |
| 3914 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 3986 | const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 3915 | 3987 | try func.genCopy(field_ty, dst_mcv, off_mcv); |
| 3916 | 3988 | break :dst dst_mcv; |
| 3917 | 3989 | }; |
| ... | ... | @@ -3972,7 +4044,7 @@ fn airArg(func: *Func, inst: Air.Inst.Index) !void { |
| 3972 | 4044 | const src_mcv = func.args[arg_index]; |
| 3973 | 4045 | const arg_ty = func.typeOfIndex(inst); |
| 3974 | 4046 | |
| 3975 | | const dst_mcv = try func.allocRegOrMem(inst, false); |
| 4047 | const dst_mcv = try func.allocRegOrMem(arg_ty, inst, false); |
| 3976 | 4048 | |
| 3977 | 4049 | log.debug("airArg {} -> {}", .{ src_mcv, dst_mcv }); |
| 3978 | 4050 | |
| ... | ... | @@ -4004,13 +4076,13 @@ fn airBreakpoint(func: *Func) !void { |
| 4004 | 4076 | } |
| 4005 | 4077 | |
| 4006 | 4078 | fn airRetAddr(func: *Func, inst: Air.Inst.Index) !void { |
| 4007 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 4079 | const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 4008 | 4080 | try func.genCopy(Type.usize, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } }); |
| 4009 | 4081 | return func.finishAir(inst, dst_mcv, .{ .none, .none, .none }); |
| 4010 | 4082 | } |
| 4011 | 4083 | |
| 4012 | 4084 | fn airFrameAddress(func: *Func, inst: Air.Inst.Index) !void { |
| 4013 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 4085 | const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 4014 | 4086 | try func.genCopy(Type.usize, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } }); |
| 4015 | 4087 | return func.finishAir(inst, dst_mcv, .{ .none, .none, .none }); |
| 4016 | 4088 | } |
| ... | ... | @@ -4196,10 +4268,7 @@ fn genCall( |
| 4196 | 4268 | if (func.mod.pic) { |
| 4197 | 4269 | return func.fail("TODO: genCall pic", .{}); |
| 4198 | 4270 | } else { |
| 4199 | | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); |
| 4200 | | const got_addr = sym.zigGotAddress(elf_file); |
| 4201 | | try func.genSetReg(Type.usize, .ra, .{ .memory = @intCast(got_addr) }); |
| 4202 | | |
| 4271 | try func.genSetReg(Type.usize, .ra, .{ .load_symbol = .{ .sym = sym.esym_index } }); |
| 4203 | 4272 | _ = try func.addInst(.{ |
| 4204 | 4273 | .tag = .jalr, |
| 4205 | 4274 | .ops = .rri, |
| ... | ... | @@ -4323,14 +4392,11 @@ fn airRetLoad(func: *Func, inst: Air.Inst.Index) !void { |
| 4323 | 4392 | try func.exitlude_jump_relocs.append(func.gpa, index); |
| 4324 | 4393 | } |
| 4325 | 4394 | |
| 4326 | | fn airCmp(func: *Func, inst: Air.Inst.Index) !void { |
| 4327 | | const tag = func.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 4395 | fn airCmp(func: *Func, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { |
| 4328 | 4396 | const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4329 | 4397 | const zcu = func.bin_file.comp.module.?; |
| 4330 | 4398 | |
| 4331 | 4399 | const result: MCValue = if (func.liveness.isUnused(inst)) .unreach else result: { |
| 4332 | | const lhs = try func.resolveInst(bin_op.lhs); |
| 4333 | | const rhs = try func.resolveInst(bin_op.rhs); |
| 4334 | 4400 | const lhs_ty = func.typeOf(bin_op.lhs); |
| 4335 | 4401 | |
| 4336 | 4402 | switch (lhs_ty.zigTypeTag(zcu)) { |
| ... | ... | @@ -4346,7 +4412,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index) !void { |
| 4346 | 4412 | .Int => lhs_ty, |
| 4347 | 4413 | .Bool => Type.u1, |
| 4348 | 4414 | .Pointer => Type.usize, |
| 4349 | | .ErrorSet => Type.u16, |
| 4415 | .ErrorSet => Type.anyerror, |
| 4350 | 4416 | .Optional => blk: { |
| 4351 | 4417 | const payload_ty = lhs_ty.optionalChild(zcu); |
| 4352 | 4418 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { |
| ... | ... | @@ -4362,7 +4428,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index) !void { |
| 4362 | 4428 | |
| 4363 | 4429 | const int_info = int_ty.intInfo(zcu); |
| 4364 | 4430 | if (int_info.bits <= 64) { |
| 4365 | | break :result try func.binOp(tag, lhs, int_ty, rhs, int_ty); |
| 4431 | break :result try func.binOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 4366 | 4432 | } else { |
| 4367 | 4433 | return func.fail("TODO riscv cmp for ints > 64 bits", .{}); |
| 4368 | 4434 | } |
| ... | ... | @@ -4373,7 +4439,7 @@ fn airCmp(func: *Func, inst: Air.Inst.Index) !void { |
| 4373 | 4439 | if (float_bits > float_reg_size) { |
| 4374 | 4440 | return func.fail("TODO: airCmp float > 64/32 bits", .{}); |
| 4375 | 4441 | } |
| 4376 | | break :result try func.binOpFloat(tag, lhs, lhs_ty, rhs, lhs_ty); |
| 4442 | break :result try func.binOp(inst, tag, bin_op.lhs, bin_op.rhs); |
| 4377 | 4443 | }, |
| 4378 | 4444 | else => unreachable, |
| 4379 | 4445 | } |
| ... | ... | @@ -4537,7 +4603,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC |
| 4537 | 4603 | else |
| 4538 | 4604 | .{ .off = @intCast(pl_ty.abiSize(zcu)), .ty = Type.bool }; |
| 4539 | 4605 | |
| 4540 | | const return_mcv = try func.allocRegOrMem(inst, true); |
| 4606 | const return_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 4541 | 4607 | assert(return_mcv == .register); // should not be larger 8 bytes |
| 4542 | 4608 | const return_reg = return_mcv.register; |
| 4543 | 4609 | |
| ... | ... | @@ -4569,7 +4635,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC |
| 4569 | 4635 | some_info.ty, |
| 4570 | 4636 | .{ .immediate = 0 }, |
| 4571 | 4637 | ), |
| 4572 | | .size = .byte, |
| 4638 | .ty = Type.bool, |
| 4573 | 4639 | }, |
| 4574 | 4640 | }, |
| 4575 | 4641 | }); |
| ... | ... | @@ -4601,7 +4667,7 @@ fn isNull(func: *Func, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC |
| 4601 | 4667 | some_info.ty, |
| 4602 | 4668 | .{ .immediate = 0 }, |
| 4603 | 4669 | ), |
| 4604 | | .size = .byte, |
| 4670 | .ty = Type.bool, |
| 4605 | 4671 | }, |
| 4606 | 4672 | }, |
| 4607 | 4673 | }); |
| ... | ... | @@ -4623,9 +4689,9 @@ fn airIsNull(func: *Func, inst: Air.Inst.Index) !void { |
| 4623 | 4689 | fn airIsNullPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4624 | 4690 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4625 | 4691 | const operand = try func.resolveInst(un_op); |
| 4626 | | _ = operand; // autofix |
| 4692 | _ = operand; |
| 4627 | 4693 | const ty = func.typeOf(un_op); |
| 4628 | | _ = ty; // autofix |
| 4694 | _ = ty; |
| 4629 | 4695 | |
| 4630 | 4696 | if (true) return func.fail("TODO: airIsNullPtr", .{}); |
| 4631 | 4697 | |
| ... | ... | @@ -4656,9 +4722,9 @@ fn airIsNonNull(func: *Func, inst: Air.Inst.Index) !void { |
| 4656 | 4722 | fn airIsNonNullPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4657 | 4723 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 4658 | 4724 | const operand = try func.resolveInst(un_op); |
| 4659 | | _ = operand; // autofix |
| 4725 | _ = operand; |
| 4660 | 4726 | const ty = func.typeOf(un_op); |
| 4661 | | _ = ty; // autofix |
| 4727 | _ = ty; |
| 4662 | 4728 | |
| 4663 | 4729 | if (true) return func.fail("TODO: airIsNonNullPtr", .{}); |
| 4664 | 4730 | |
| ... | ... | @@ -4685,7 +4751,7 @@ fn airIsErrPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4685 | 4751 | // The MCValue that holds the pointer can be re-used as the value. |
| 4686 | 4752 | break :blk operand_ptr; |
| 4687 | 4753 | } else { |
| 4688 | | break :blk try func.allocRegOrMem(inst, true); |
| 4754 | break :blk try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 4689 | 4755 | } |
| 4690 | 4756 | }; |
| 4691 | 4757 | try func.load(operand, operand_ptr, func.typeOf(un_op)); |
| ... | ... | @@ -4701,45 +4767,44 @@ fn airIsErrPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4701 | 4767 | /// |
| 4702 | 4768 | /// Result is in the return register. |
| 4703 | 4769 | fn isErr(func: *Func, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MCValue { |
| 4770 | _ = maybe_inst; |
| 4704 | 4771 | const zcu = func.bin_file.comp.module.?; |
| 4705 | 4772 | const err_ty = eu_ty.errorUnionSet(zcu); |
| 4706 | 4773 | if (err_ty.errorSetIsEmpty(zcu)) return MCValue{ .immediate = 0 }; // always false |
| 4707 | | |
| 4708 | | _ = maybe_inst; |
| 4709 | | |
| 4710 | 4774 | const err_off: u31 = @intCast(errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu)); |
| 4711 | 4775 | |
| 4776 | const return_reg, const return_lock = try func.allocReg(.int); |
| 4777 | defer func.register_manager.unlockReg(return_lock); |
| 4778 | |
| 4712 | 4779 | switch (eu_mcv) { |
| 4713 | 4780 | .register => |reg| { |
| 4714 | 4781 | const eu_lock = func.register_manager.lockReg(reg); |
| 4715 | 4782 | defer if (eu_lock) |lock| func.register_manager.unlockReg(lock); |
| 4716 | 4783 | |
| 4717 | | const return_reg = try func.copyToTmpRegister(eu_ty, eu_mcv); |
| 4718 | | const return_lock = func.register_manager.lockRegAssumeUnused(return_reg); |
| 4719 | | defer func.register_manager.unlockReg(return_lock); |
| 4720 | | |
| 4721 | | var return_mcv: MCValue = .{ .register = return_reg }; |
| 4784 | try func.genCopy(eu_ty, .{ .register = return_reg }, eu_mcv); |
| 4722 | 4785 | |
| 4723 | 4786 | if (err_off > 0) { |
| 4724 | | return_mcv = try func.binOp( |
| 4787 | try func.genBinOp( |
| 4725 | 4788 | .shr, |
| 4726 | | return_mcv, |
| 4789 | .{ .register = return_reg }, |
| 4727 | 4790 | eu_ty, |
| 4728 | 4791 | .{ .immediate = @as(u6, @intCast(err_off * 8)) }, |
| 4729 | 4792 | Type.u8, |
| 4793 | return_reg, |
| 4730 | 4794 | ); |
| 4731 | 4795 | } |
| 4732 | 4796 | |
| 4733 | | return try func.binOp( |
| 4797 | try func.genBinOp( |
| 4734 | 4798 | .cmp_neq, |
| 4735 | | return_mcv, |
| 4736 | | Type.u16, |
| 4799 | .{ .register = return_reg }, |
| 4800 | Type.anyerror, |
| 4737 | 4801 | .{ .immediate = 0 }, |
| 4738 | | Type.u16, |
| 4802 | Type.u8, |
| 4803 | return_reg, |
| 4739 | 4804 | ); |
| 4740 | 4805 | }, |
| 4741 | 4806 | .load_frame => |frame_addr| { |
| 4742 | | return func.binOp( |
| 4807 | try func.genBinOp( |
| 4743 | 4808 | .cmp_neq, |
| 4744 | 4809 | .{ .load_frame = .{ |
| 4745 | 4810 | .index = frame_addr.index, |
| ... | ... | @@ -4748,10 +4813,13 @@ fn isErr(func: *Func, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) |
| 4748 | 4813 | Type.anyerror, |
| 4749 | 4814 | .{ .immediate = 0 }, |
| 4750 | 4815 | Type.anyerror, |
| 4816 | return_reg, |
| 4751 | 4817 | ); |
| 4752 | 4818 | }, |
| 4753 | 4819 | else => return func.fail("TODO implement isErr for {}", .{eu_mcv}), |
| 4754 | 4820 | } |
| 4821 | |
| 4822 | return .{ .register = return_reg }; |
| 4755 | 4823 | } |
| 4756 | 4824 | |
| 4757 | 4825 | fn airIsNonErr(func: *Func, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -4799,7 +4867,7 @@ fn airIsNonErrPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 4799 | 4867 | // The MCValue that holds the pointer can be re-used as the value. |
| 4800 | 4868 | break :blk operand_ptr; |
| 4801 | 4869 | } else { |
| 4802 | | break :blk try func.allocRegOrMem(inst, true); |
| 4870 | break :blk try func.allocRegOrMem(func.typeOfIndex(inst), inst, true); |
| 4803 | 4871 | } |
| 4804 | 4872 | }; |
| 4805 | 4873 | const operand_ptr_ty = func.typeOf(un_op); |
| ... | ... | @@ -4916,16 +4984,18 @@ fn airSwitchBr(func: *Func, inst: Air.Inst.Index) !void { |
| 4916 | 4984 | // switch branches must be comptime-known, so this is stored in an immediate |
| 4917 | 4985 | const item_mcv = try func.resolveInst(item); |
| 4918 | 4986 | |
| 4919 | | const cmp_mcv: MCValue = try func.binOp( |
| 4987 | const cmp_reg, const cmp_lock = try func.allocReg(.int); |
| 4988 | defer func.register_manager.unlockReg(cmp_lock); |
| 4989 | |
| 4990 | try func.genBinOp( |
| 4920 | 4991 | .cmp_neq, |
| 4921 | 4992 | condition, |
| 4922 | 4993 | condition_ty, |
| 4923 | 4994 | item_mcv, |
| 4924 | 4995 | condition_ty, |
| 4996 | cmp_reg, |
| 4925 | 4997 | ); |
| 4926 | 4998 | |
| 4927 | | const cmp_reg = try func.copyToTmpRegister(Type.bool, cmp_mcv); |
| 4928 | | |
| 4929 | 4999 | if (!(i < relocs.len - 1)) { |
| 4930 | 5000 | _ = try func.addInst(.{ |
| 4931 | 5001 | .tag = .pseudo, |
| ... | ... | @@ -5019,7 +5089,7 @@ fn airBr(func: *Func, inst: Air.Inst.Index) !void { |
| 5019 | 5089 | break :result block_tracking.short; |
| 5020 | 5090 | } |
| 5021 | 5091 | |
| 5022 | | const dst_mcv = if (first_br) try func.allocRegOrMem(br.block_inst, true) else dst: { |
| 5092 | const dst_mcv = if (first_br) try func.allocRegOrMem(block_ty, br.block_inst, true) else dst: { |
| 5023 | 5093 | try func.getValue(block_tracking.short, br.block_inst); |
| 5024 | 5094 | break :dst block_tracking.short; |
| 5025 | 5095 | }; |
| ... | ... | @@ -5063,10 +5133,10 @@ fn airBoolOp(func: *Func, inst: Air.Inst.Index) !void { |
| 5063 | 5133 | const lhs_ty = Type.bool; |
| 5064 | 5134 | const rhs_ty = Type.bool; |
| 5065 | 5135 | |
| 5066 | | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs, .{}); |
| 5136 | const lhs_reg, const lhs_lock = try func.promoteReg(lhs_ty, lhs); |
| 5067 | 5137 | defer if (lhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 5068 | 5138 | |
| 5069 | | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs, .{}); |
| 5139 | const rhs_reg, const rhs_lock = try func.promoteReg(rhs_ty, rhs); |
| 5070 | 5140 | defer if (rhs_lock) |lock| func.register_manager.unlockReg(lock); |
| 5071 | 5141 | |
| 5072 | 5142 | const result_reg, const result_lock = try func.allocReg(.int); |
| ... | ... | @@ -5262,7 +5332,7 @@ fn genCopy(func: *Func, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 5262 | 5332 | const src_info: ?struct { addr_reg: Register, addr_lock: ?RegisterLock } = switch (src_mcv) { |
| 5263 | 5333 | .register_pair, .memory, .indirect, .load_frame => null, |
| 5264 | 5334 | .load_symbol => src: { |
| 5265 | | const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.usize, src_mcv.address(), .{}); |
| 5335 | const src_addr_reg, const src_addr_lock = try func.promoteReg(Type.usize, src_mcv.address()); |
| 5266 | 5336 | errdefer func.register_manager.unlockReg(src_addr_lock); |
| 5267 | 5337 | |
| 5268 | 5338 | break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; |
| ... | ... | @@ -5557,7 +5627,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5557 | 5627 | .data = .{ .i_type = .{ |
| 5558 | 5628 | .rd = reg, |
| 5559 | 5629 | .rs1 = reg, |
| 5560 | | .imm12 = Immediate.s(32), |
| 5630 | .imm12 = Immediate.u(32), |
| 5561 | 5631 | } }, |
| 5562 | 5632 | }); |
| 5563 | 5633 | |
| ... | ... | @@ -5604,10 +5674,9 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5604 | 5674 | .m = .{ |
| 5605 | 5675 | .base = .{ .frame = frame.index }, |
| 5606 | 5676 | .mod = .{ |
| 5607 | | .rm = .{ |
| 5608 | | .size = func.memSize(ty), |
| 5609 | | .disp = frame.off, |
| 5610 | | }, |
| 5677 | .size = func.memSize(ty), |
| 5678 | .unsigned = ty.isUnsignedInt(zcu), |
| 5679 | .disp = frame.off, |
| 5611 | 5680 | }, |
| 5612 | 5681 | }, |
| 5613 | 5682 | } }, |
| ... | ... | @@ -5622,7 +5691,7 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5622 | 5691 | .data = .{ .i_type = .{ |
| 5623 | 5692 | .rd = reg, |
| 5624 | 5693 | .rs1 = reg, |
| 5625 | | .imm12 = Immediate.s(0), |
| 5694 | .imm12 = Immediate.u(0), |
| 5626 | 5695 | } }, |
| 5627 | 5696 | }); |
| 5628 | 5697 | }, |
| ... | ... | @@ -5636,19 +5705,17 @@ fn genSetReg(func: *Func, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 5636 | 5705 | .register_offset => |reg_off| .{ |
| 5637 | 5706 | .base = .{ .reg = reg_off.reg }, |
| 5638 | 5707 | .mod = .{ |
| 5639 | | .rm = .{ |
| 5640 | | .size = func.memSize(ty), |
| 5641 | | .disp = reg_off.off, |
| 5642 | | }, |
| 5708 | .size = func.memSize(ty), |
| 5709 | .disp = reg_off.off, |
| 5710 | .unsigned = false, |
| 5643 | 5711 | }, |
| 5644 | 5712 | }, |
| 5645 | 5713 | .lea_frame => |frame| .{ |
| 5646 | 5714 | .base = .{ .frame = frame.index }, |
| 5647 | 5715 | .mod = .{ |
| 5648 | | .rm = .{ |
| 5649 | | .size = func.memSize(ty), |
| 5650 | | .disp = frame.off, |
| 5651 | | }, |
| 5716 | .size = func.memSize(ty), |
| 5717 | .disp = frame.off, |
| 5718 | .unsigned = false, |
| 5652 | 5719 | }, |
| 5653 | 5720 | }, |
| 5654 | 5721 | else => unreachable, |
| ... | ... | @@ -5787,9 +5854,10 @@ fn genSetMem( |
| 5787 | 5854 | .r = reg, |
| 5788 | 5855 | .m = .{ |
| 5789 | 5856 | .base = .{ .frame = frame_index }, |
| 5790 | | .mod = .{ .rm = .{ |
| 5857 | .mod = .{ |
| 5791 | 5858 | .size = Memory.Size.fromByteSize(src_size), |
| 5792 | | } }, |
| 5859 | .unsigned = false, |
| 5860 | }, |
| 5793 | 5861 | }, |
| 5794 | 5862 | } }, |
| 5795 | 5863 | }); |
| ... | ... | @@ -5802,10 +5870,11 @@ fn genSetMem( |
| 5802 | 5870 | .r = reg, |
| 5803 | 5871 | .m = .{ |
| 5804 | 5872 | .base = base, |
| 5805 | | .mod = .{ .rm = .{ |
| 5873 | .mod = .{ |
| 5806 | 5874 | .size = func.memSize(ty), |
| 5807 | 5875 | .disp = disp, |
| 5808 | | } }, |
| 5876 | .unsigned = false, |
| 5877 | }, |
| 5809 | 5878 | }, |
| 5810 | 5879 | } }, |
| 5811 | 5880 | }); |
| ... | ... | @@ -5820,7 +5889,7 @@ fn genSetMem( |
| 5820 | 5889 | .immediate => { |
| 5821 | 5890 | // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with |
| 5822 | 5891 | // a register allocation. |
| 5823 | | const reg, const reg_lock = try func.promoteReg(ty, src_mcv, .{}); |
| 5892 | const reg, const reg_lock = try func.promoteReg(ty, src_mcv); |
| 5824 | 5893 | defer if (reg_lock) |lock| func.register_manager.unlockReg(lock); |
| 5825 | 5894 | |
| 5826 | 5895 | return func.genSetMem(base, disp, ty, .{ .register = reg }); |
| ... | ... | @@ -5833,9 +5902,10 @@ fn airIntFromPtr(func: *Func, inst: Air.Inst.Index) !void { |
| 5833 | 5902 | const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; |
| 5834 | 5903 | const result = result: { |
| 5835 | 5904 | const src_mcv = try func.resolveInst(un_op); |
| 5905 | const src_ty = func.typeOfIndex(inst); |
| 5836 | 5906 | if (func.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv; |
| 5837 | 5907 | |
| 5838 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 5908 | const dst_mcv = try func.allocRegOrMem(src_ty, inst, true); |
| 5839 | 5909 | const dst_ty = func.typeOfIndex(inst); |
| 5840 | 5910 | try func.genCopy(dst_ty, dst_mcv, src_mcv); |
| 5841 | 5911 | break :result dst_mcv; |
| ... | ... | @@ -5858,7 +5928,7 @@ fn airBitCast(func: *Func, inst: Air.Inst.Index) !void { |
| 5858 | 5928 | |
| 5859 | 5929 | const dst_mcv = if (dst_ty.abiSize(zcu) <= src_ty.abiSize(zcu) and |
| 5860 | 5930 | func.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { |
| 5861 | | const dst_mcv = try func.allocRegOrMem(inst, true); |
| 5931 | const dst_mcv = try func.allocRegOrMem(dst_ty, inst, true); |
| 5862 | 5932 | try func.genCopy(switch (math.order(dst_ty.abiSize(zcu), src_ty.abiSize(zcu))) { |
| 5863 | 5933 | .lt => dst_ty, |
| 5864 | 5934 | .eq => if (!dst_mcv.isMemory() or src_mcv.isMemory()) dst_ty else src_ty, |
| ... | ... | @@ -6080,7 +6150,7 @@ fn airErrorName(func: *Func, inst: Air.Inst.Index) !void { |
| 6080 | 6150 | .tag = .slli, |
| 6081 | 6151 | .ops = .rri, |
| 6082 | 6152 | .data = .{ .i_type = .{ |
| 6083 | | .imm12 = Immediate.s(4), |
| 6153 | .imm12 = Immediate.u(4), |
| 6084 | 6154 | .rd = err_reg, |
| 6085 | 6155 | .rs1 = err_reg, |
| 6086 | 6156 | } }, |
| ... | ... | @@ -6104,7 +6174,7 @@ fn airErrorName(func: *Func, inst: Air.Inst.Index) !void { |
| 6104 | 6174 | .r = start_reg, |
| 6105 | 6175 | .m = .{ |
| 6106 | 6176 | .base = .{ .reg = addr_reg }, |
| 6107 | | .mod = .{ .off = 0 }, |
| 6177 | .mod = .{ .size = .dword, .unsigned = true }, |
| 6108 | 6178 | }, |
| 6109 | 6179 | }, |
| 6110 | 6180 | }, |
| ... | ... | @@ -6118,13 +6188,13 @@ fn airErrorName(func: *Func, inst: Air.Inst.Index) !void { |
| 6118 | 6188 | .r = end_reg, |
| 6119 | 6189 | .m = .{ |
| 6120 | 6190 | .base = .{ .reg = addr_reg }, |
| 6121 | | .mod = .{ .off = 8 }, |
| 6191 | .mod = .{ .size = .dword, .unsigned = true }, |
| 6122 | 6192 | }, |
| 6123 | 6193 | }, |
| 6124 | 6194 | }, |
| 6125 | 6195 | }); |
| 6126 | 6196 | |
| 6127 | | const dst_mcv = try func.allocRegOrMem(inst, false); |
| 6197 | const dst_mcv = try func.allocRegOrMem(func.typeOfIndex(inst), inst, false); |
| 6128 | 6198 | const frame = dst_mcv.load_frame; |
| 6129 | 6199 | try func.genSetMem( |
| 6130 | 6200 | .{ .frame = frame.index }, |