| ... | ... | @@ -25,6 +25,7 @@ const Type = @import("../../type.zig").Type; |
| 25 | 25 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 26 | 26 | |
| 27 | 27 | mir: Mir, |
| 28 | bin_file: *link.File, |
| 28 | 29 | |
| 29 | 30 | pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index), air: Air) !void { |
| 30 | 31 | const instruction_bytes = print.mir.instructions.len * |
| ... | ... | @@ -116,15 +117,15 @@ pub fn printMir(print: *const Print, w: anytype, mir_to_air_map: std.AutoHashMap |
| 116 | 117 | .jmp => try print.mirJmpCall(.jmp, inst, w), |
| 117 | 118 | .call => try print.mirJmpCall(.call, inst, w), |
| 118 | 119 | |
| 119 | | // .cond_jmp_greater_less => try print.mirCondJmp(.cond_jmp_greater_less, inst, w), |
| 120 | | // .cond_jmp_above_below => try print.mirCondJmp(.cond_jmp_above_below, inst, w), |
| 121 | | // .cond_jmp_eq_ne => try print.mirCondJmp(.cond_jmp_eq_ne, inst, w), |
| 120 | .cond_jmp_greater_less => try print.mirCondJmp(.cond_jmp_greater_less, inst, w), |
| 121 | .cond_jmp_above_below => try print.mirCondJmp(.cond_jmp_above_below, inst, w), |
| 122 | .cond_jmp_eq_ne => try print.mirCondJmp(.cond_jmp_eq_ne, inst, w), |
| 122 | 123 | |
| 123 | | // .cond_set_byte_greater_less => try print.mirCondSetByte(.cond_set_byte_greater_less, inst, w), |
| 124 | | // .cond_set_byte_above_below => try print.mirCondSetByte(.cond_set_byte_above_below, inst, w), |
| 125 | | // .cond_set_byte_eq_ne => try print.mirCondSetByte(.cond_set_byte_eq_ne, inst, w), |
| 124 | .cond_set_byte_greater_less => try print.mirCondSetByte(.cond_set_byte_greater_less, inst, w), |
| 125 | .cond_set_byte_above_below => try print.mirCondSetByte(.cond_set_byte_above_below, inst, w), |
| 126 | .cond_set_byte_eq_ne => try print.mirCondSetByte(.cond_set_byte_eq_ne, inst, w), |
| 126 | 127 | |
| 127 | | // .@"test" => try print.mirTest(inst, w), |
| 128 | .@"test" => try print.mirTest(inst, w), |
| 128 | 129 | |
| 129 | 130 | .brk => try w.writeAll("brk\n"), |
| 130 | 131 | .ret => try w.writeAll("ret\n"), |
| ... | ... | @@ -209,254 +210,24 @@ fn mirJmpCall(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: a |
| 209 | 210 | try w.print("{s}\n", .{@tagName(ops.reg1)}); |
| 210 | 211 | } |
| 211 | 212 | |
| 212 | | const CondType = enum { |
| 213 | | /// greater than or equal |
| 214 | | gte, |
| 215 | | |
| 216 | | /// greater than |
| 217 | | gt, |
| 218 | | |
| 219 | | /// less than |
| 220 | | lt, |
| 221 | | |
| 222 | | /// less than or equal |
| 223 | | lte, |
| 224 | | |
| 225 | | /// above or equal |
| 226 | | ae, |
| 227 | | |
| 228 | | /// above |
| 229 | | a, |
| 230 | | |
| 231 | | /// below |
| 232 | | b, |
| 233 | | |
| 234 | | /// below or equal |
| 235 | | be, |
| 236 | | |
| 237 | | /// not equal |
| 238 | | ne, |
| 239 | | |
| 240 | | /// equal |
| 241 | | eq, |
| 242 | | |
| 243 | | fn fromTagAndFlags(tag: Mir.Inst.Tag, flags: u2) CondType { |
| 244 | | return switch (tag) { |
| 245 | | .cond_jmp_greater_less, |
| 246 | | .cond_set_byte_greater_less, |
| 247 | | => switch (flags) { |
| 248 | | 0b00 => CondType.gte, |
| 249 | | 0b01 => CondType.gt, |
| 250 | | 0b10 => CondType.lt, |
| 251 | | 0b11 => CondType.lte, |
| 252 | | }, |
| 253 | | .cond_jmp_above_below, |
| 254 | | .cond_set_byte_above_below, |
| 255 | | => switch (flags) { |
| 256 | | 0b00 => CondType.ae, |
| 257 | | 0b01 => CondType.a, |
| 258 | | 0b10 => CondType.b, |
| 259 | | 0b11 => CondType.be, |
| 260 | | }, |
| 261 | | .cond_jmp_eq_ne, |
| 262 | | .cond_set_byte_eq_ne, |
| 263 | | => switch (@truncate(u1, flags)) { |
| 264 | | 0b0 => CondType.ne, |
| 265 | | 0b1 => CondType.eq, |
| 266 | | }, |
| 267 | | else => unreachable, |
| 268 | | }; |
| 269 | | } |
| 270 | | }; |
| 271 | | |
| 272 | | inline fn getCondOpCode(tag: Mir.Inst.Tag, cond: CondType) u8 { |
| 273 | | switch (cond) { |
| 274 | | .gte => return switch (tag) { |
| 275 | | .cond_jmp_greater_less => 0x8d, |
| 276 | | .cond_set_byte_greater_less => 0x9d, |
| 277 | | else => unreachable, |
| 278 | | }, |
| 279 | | .gt => return switch (tag) { |
| 280 | | .cond_jmp_greater_less => 0x8f, |
| 281 | | .cond_set_byte_greater_less => 0x9f, |
| 282 | | else => unreachable, |
| 283 | | }, |
| 284 | | .lt => return switch (tag) { |
| 285 | | .cond_jmp_greater_less => 0x8c, |
| 286 | | .cond_set_byte_greater_less => 0x9c, |
| 287 | | else => unreachable, |
| 288 | | }, |
| 289 | | .lte => return switch (tag) { |
| 290 | | .cond_jmp_greater_less => 0x8e, |
| 291 | | .cond_set_byte_greater_less => 0x9e, |
| 292 | | else => unreachable, |
| 293 | | }, |
| 294 | | .ae => return switch (tag) { |
| 295 | | .cond_jmp_above_below => 0x83, |
| 296 | | .cond_set_byte_above_below => 0x93, |
| 297 | | else => unreachable, |
| 298 | | }, |
| 299 | | .a => return switch (tag) { |
| 300 | | .cond_jmp_above_below => 0x87, |
| 301 | | .cond_set_byte_greater_less => 0x97, |
| 302 | | else => unreachable, |
| 303 | | }, |
| 304 | | .b => return switch (tag) { |
| 305 | | .cond_jmp_above_below => 0x82, |
| 306 | | .cond_set_byte_greater_less => 0x92, |
| 307 | | else => unreachable, |
| 308 | | }, |
| 309 | | .be => return switch (tag) { |
| 310 | | .cond_jmp_above_below => 0x86, |
| 311 | | .cond_set_byte_greater_less => 0x96, |
| 312 | | else => unreachable, |
| 313 | | }, |
| 314 | | .eq => return switch (tag) { |
| 315 | | .cond_jmp_eq_ne => 0x84, |
| 316 | | .cond_set_byte_eq_ne => 0x94, |
| 317 | | else => unreachable, |
| 318 | | }, |
| 319 | | .ne => return switch (tag) { |
| 320 | | .cond_jmp_eq_ne => 0x85, |
| 321 | | .cond_set_byte_eq_ne => 0x95, |
| 322 | | else => unreachable, |
| 323 | | }, |
| 324 | | } |
| 325 | | } |
| 326 | | |
| 327 | 213 | fn mirCondJmp(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { |
| 328 | | _ = w; // TODO |
| 329 | | const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 330 | | const target = print.mir.instructions.items(.data)[inst].inst; |
| 331 | | const cond = CondType.fromTagAndFlags(tag, ops.flags); |
| 332 | | const opc = getCondOpCode(tag, cond); |
| 333 | | const source = print.code.items.len; |
| 334 | | const encoder = try Encoder.init(print.code, 6); |
| 335 | | encoder.opcode_2byte(0x0f, opc); |
| 336 | | try print.relocs.append(print.bin_file.allocator, .{ |
| 337 | | .source = source, |
| 338 | | .target = target, |
| 339 | | .offset = print.code.items.len, |
| 340 | | .length = 6, |
| 341 | | }); |
| 342 | | encoder.imm32(0); |
| 214 | _ = print; |
| 215 | _ = tag; |
| 216 | _ = inst; |
| 217 | try w.writeAll("TODO print mirCondJmp\n"); |
| 343 | 218 | } |
| 344 | 219 | |
| 345 | 220 | fn mirCondSetByte(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { |
| 346 | | _ = w; // TODO |
| 347 | | const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 348 | | const cond = CondType.fromTagAndFlags(tag, ops.flags); |
| 349 | | const opc = getCondOpCode(tag, cond); |
| 350 | | const encoder = try Encoder.init(print.code, 4); |
| 351 | | encoder.rex(.{ |
| 352 | | .w = true, |
| 353 | | .b = ops.reg1.isExtended(), |
| 354 | | }); |
| 355 | | encoder.opcode_2byte(0x0f, opc); |
| 356 | | encoder.modRm_direct(0x0, ops.reg1.lowId()); |
| 221 | _ = tag; |
| 222 | _ = inst; |
| 223 | _ = print; |
| 224 | try w.writeAll("TODO print mirCondSetByte\n"); |
| 357 | 225 | } |
| 358 | 226 | |
| 359 | 227 | fn mirTest(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { |
| 360 | | _ = w; // TODO |
| 361 | | const tag = print.mir.instructions.items(.tag)[inst]; |
| 362 | | assert(tag == .@"test"); |
| 363 | | const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 364 | | switch (ops.flags) { |
| 365 | | 0b00 => blk: { |
| 366 | | if (ops.reg2 == .none) { |
| 367 | | // TEST r/m64, imm32 |
| 368 | | const imm = print.mir.instructions.items(.data)[inst].imm; |
| 369 | | if (ops.reg1.to64() == .rax) { |
| 370 | | // TODO reduce the size of the instruction if the immediate |
| 371 | | // is smaller than 32 bits |
| 372 | | const encoder = try Encoder.init(print.code, 6); |
| 373 | | encoder.rex(.{ |
| 374 | | .w = true, |
| 375 | | }); |
| 376 | | encoder.opcode_1byte(0xa9); |
| 377 | | encoder.imm32(imm); |
| 378 | | break :blk; |
| 379 | | } |
| 380 | | const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7; |
| 381 | | const encoder = try Encoder.init(print.code, 7); |
| 382 | | encoder.rex(.{ |
| 383 | | .w = true, |
| 384 | | .b = ops.reg1.isExtended(), |
| 385 | | }); |
| 386 | | encoder.opcode_1byte(opc); |
| 387 | | encoder.modRm_direct(0, ops.reg1.lowId()); |
| 388 | | encoder.imm8(@intCast(i8, imm)); |
| 389 | | break :blk; |
| 390 | | } |
| 391 | | // TEST r/m64, r64 |
| 392 | | return print.fail("TODO TEST r/m64, r64", .{}); |
| 393 | | }, |
| 394 | | else => return print.fail("TODO more TEST alternatives", .{}), |
| 395 | | } |
| 396 | | } |
| 397 | | |
| 398 | | const EncType = enum { |
| 399 | | /// OP r/m64, imm32 |
| 400 | | mi, |
| 401 | | |
| 402 | | /// OP r/m64, r64 |
| 403 | | mr, |
| 404 | | |
| 405 | | /// OP r64, r/m64 |
| 406 | | rm, |
| 407 | | }; |
| 408 | | |
| 409 | | const OpCode = struct { |
| 410 | | opc: u8, |
| 411 | | /// Only used if `EncType == .mi`. |
| 412 | | modrm_ext: u3, |
| 413 | | }; |
| 414 | | |
| 415 | | inline fn getArithOpCode(tag: Mir.Inst.Tag, enc: EncType) OpCode { |
| 416 | | switch (enc) { |
| 417 | | .mi => return switch (tag) { |
| 418 | | .adc => .{ .opc = 0x81, .modrm_ext = 0x2 }, |
| 419 | | .add => .{ .opc = 0x81, .modrm_ext = 0x0 }, |
| 420 | | .sub => .{ .opc = 0x81, .modrm_ext = 0x5 }, |
| 421 | | .xor => .{ .opc = 0x81, .modrm_ext = 0x6 }, |
| 422 | | .@"and" => .{ .opc = 0x81, .modrm_ext = 0x4 }, |
| 423 | | .@"or" => .{ .opc = 0x81, .modrm_ext = 0x1 }, |
| 424 | | .sbb => .{ .opc = 0x81, .modrm_ext = 0x3 }, |
| 425 | | .cmp => .{ .opc = 0x81, .modrm_ext = 0x7 }, |
| 426 | | .mov => .{ .opc = 0xc7, .modrm_ext = 0x0 }, |
| 427 | | else => unreachable, |
| 428 | | }, |
| 429 | | .mr => { |
| 430 | | const opc: u8 = switch (tag) { |
| 431 | | .adc => 0x11, |
| 432 | | .add => 0x01, |
| 433 | | .sub => 0x29, |
| 434 | | .xor => 0x31, |
| 435 | | .@"and" => 0x21, |
| 436 | | .@"or" => 0x09, |
| 437 | | .sbb => 0x19, |
| 438 | | .cmp => 0x39, |
| 439 | | .mov => 0x89, |
| 440 | | else => unreachable, |
| 441 | | }; |
| 442 | | return .{ .opc = opc, .modrm_ext = undefined }; |
| 443 | | }, |
| 444 | | .rm => { |
| 445 | | const opc: u8 = switch (tag) { |
| 446 | | .adc => 0x13, |
| 447 | | .add => 0x03, |
| 448 | | .sub => 0x2b, |
| 449 | | .xor => 0x33, |
| 450 | | .@"and" => 0x23, |
| 451 | | .@"or" => 0x0b, |
| 452 | | .sbb => 0x1b, |
| 453 | | .cmp => 0x3b, |
| 454 | | .mov => 0x8b, |
| 455 | | else => unreachable, |
| 456 | | }; |
| 457 | | return .{ .opc = opc, .modrm_ext = undefined }; |
| 458 | | }, |
| 459 | | } |
| 228 | _ = print; |
| 229 | _ = inst; |
| 230 | try w.writeAll("TODO print mirTest\n"); |
| 460 | 231 | } |
| 461 | 232 | |
| 462 | 233 | fn mirArith(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { |
| ... | ... | @@ -473,36 +244,61 @@ fn mirArith(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: any |
| 473 | 244 | 0b01 => { |
| 474 | 245 | const imm = print.mir.instructions.items(.data)[inst].imm; |
| 475 | 246 | if (ops.reg2 == .none) { |
| 476 | | try w.print("{s}, [ds:{d}]", .{ @tagName(ops.reg1), imm }); |
| 247 | try w.print("{s}, ", .{@tagName(ops.reg1)}); |
| 248 | switch (ops.reg1.size()) { |
| 249 | 8 => try w.print("byte ptr ", .{}), |
| 250 | 16 => try w.print("word ptr ", .{}), |
| 251 | 32 => try w.print("dword ptr ", .{}), |
| 252 | 64 => try w.print("qword ptr ", .{}), |
| 253 | else => unreachable, |
| 254 | } |
| 255 | try w.print("[ds:{d}]", .{imm}); |
| 477 | 256 | } else { |
| 478 | | try w.print("{s}, [{s} + {d}]", .{ @tagName(ops.reg1), @tagName(ops.reg2), imm }); |
| 257 | try w.print("{s}, ", .{@tagName(ops.reg1)}); |
| 258 | switch (ops.reg1.size()) { |
| 259 | 8 => try w.print("byte ptr ", .{}), |
| 260 | 16 => try w.print("word ptr ", .{}), |
| 261 | 32 => try w.print("dword ptr ", .{}), |
| 262 | 64 => try w.print("qword ptr ", .{}), |
| 263 | else => unreachable, |
| 264 | } |
| 265 | try w.print("[{s} + {d}]", .{ @tagName(ops.reg2), imm }); |
| 479 | 266 | } |
| 480 | 267 | }, |
| 481 | 268 | 0b10 => { |
| 482 | 269 | const imm = print.mir.instructions.items(.data)[inst].imm; |
| 483 | 270 | if (ops.reg2 == .none) { |
| 484 | | try w.print("[{s} + 0], {d}", .{ @tagName(ops.reg1), imm }); |
| 271 | try w.writeAll("unused variant"); |
| 485 | 272 | } else { |
| 273 | switch (ops.reg2.size()) { |
| 274 | 8 => try w.print("byte ptr ", .{}), |
| 275 | 16 => try w.print("word ptr ", .{}), |
| 276 | 32 => try w.print("dword ptr ", .{}), |
| 277 | 64 => try w.print("qword ptr ", .{}), |
| 278 | else => unreachable, |
| 279 | } |
| 486 | 280 | try w.print("[{s} + {d}], {s}", .{ @tagName(ops.reg1), imm, @tagName(ops.reg2) }); |
| 487 | 281 | } |
| 488 | 282 | }, |
| 489 | 283 | 0b11 => { |
| 490 | | if (ops.reg2 == .none) { |
| 491 | | const payload = print.mir.instructions.items(.data)[inst].payload; |
| 492 | | const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data; |
| 493 | | try w.print("[{s} + {d}], {d}", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand }); |
| 494 | | } |
| 495 | | try w.writeAll("TODO"); |
| 284 | try w.writeAll("unused variant"); |
| 496 | 285 | }, |
| 497 | 286 | } |
| 498 | 287 | try w.writeByte('\n'); |
| 499 | 288 | } |
| 500 | 289 | |
| 501 | 290 | fn mirArithMemImm(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { |
| 502 | | _ = print; |
| 503 | | _ = tag; |
| 504 | | _ = inst; |
| 505 | | return w.writeAll("TODO mirArithMemImm\n"); |
| 291 | const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 292 | const payload = print.mir.instructions.items(.data)[inst].payload; |
| 293 | const imm_pair = print.mir.extraData(Mir.ImmPair, payload).data; |
| 294 | try w.print("{s} ", .{@tagName(tag)}); |
| 295 | switch (ops.flags) { |
| 296 | 0b00 => try w.print("byte ptr ", .{}), |
| 297 | 0b01 => try w.print("word ptr ", .{}), |
| 298 | 0b10 => try w.print("dword ptr ", .{}), |
| 299 | 0b11 => try w.print("qword ptr ", .{}), |
| 300 | } |
| 301 | try w.print("[{s} + {d}], {d}\n", .{ @tagName(ops.reg1), imm_pair.dest_off, imm_pair.operand }); |
| 506 | 302 | } |
| 507 | 303 | |
| 508 | 304 | fn mirArithScaleSrc(print: *const Print, tag: Mir.Inst.Tag, inst: Mir.Inst.Index, w: anytype) !void { |
| ... | ... | @@ -575,16 +371,57 @@ fn mirIMulComplex(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { |
| 575 | 371 | } |
| 576 | 372 | |
| 577 | 373 | fn mirLea(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { |
| 578 | | _ = print; |
| 579 | | _ = inst; |
| 580 | | return w.writeAll("TODO lea\n"); |
| 581 | | // const tag = print.mir.instructions.items(.tag)[inst]; |
| 582 | | // assert(tag == .lea); |
| 583 | | // const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 584 | | // assert(ops.flags == 0b01); |
| 585 | | // const imm = print.mir.instructions.items(.data)[inst].imm; |
| 586 | | |
| 587 | | // try w.print("lea {s} [{s} + {d}]\n", .{ @tagName(ops.reg1), @tagName(ops.reg2), imm }); |
| 374 | const ops = Mir.Ops.decode(print.mir.instructions.items(.ops)[inst]); |
| 375 | try w.writeAll("lea "); |
| 376 | switch (ops.flags) { |
| 377 | 0b00 => { |
| 378 | const imm = print.mir.instructions.items(.data)[inst].imm; |
| 379 | try w.print("{s} [", .{@tagName(ops.reg1)}); |
| 380 | if (ops.reg2 != .none) { |
| 381 | try w.print("{s} + ", .{@tagName(ops.reg2)}); |
| 382 | } else { |
| 383 | try w.print("ds:", .{}); |
| 384 | } |
| 385 | try w.print("{d}]\n", .{imm}); |
| 386 | }, |
| 387 | 0b01 => { |
| 388 | try w.print("{s}, ", .{@tagName(ops.reg1)}); |
| 389 | switch (ops.reg1.size()) { |
| 390 | 8 => try w.print("byte ptr ", .{}), |
| 391 | 16 => try w.print("word ptr ", .{}), |
| 392 | 32 => try w.print("dword ptr ", .{}), |
| 393 | 64 => try w.print("qword ptr ", .{}), |
| 394 | else => unreachable, |
| 395 | } |
| 396 | try w.print("[rip + 0x0] ", .{}); |
| 397 | const payload = print.mir.instructions.items(.data)[inst].payload; |
| 398 | const imm = print.mir.extraData(Mir.Imm64, payload).data.decode(); |
| 399 | try w.print("target@{x}", .{imm}); |
| 400 | }, |
| 401 | 0b10 => { |
| 402 | try w.print("{s}, ", .{@tagName(ops.reg1)}); |
| 403 | switch (ops.reg1.size()) { |
| 404 | 8 => try w.print("byte ptr ", .{}), |
| 405 | 16 => try w.print("word ptr ", .{}), |
| 406 | 32 => try w.print("dword ptr ", .{}), |
| 407 | 64 => try w.print("qword ptr ", .{}), |
| 408 | else => unreachable, |
| 409 | } |
| 410 | try w.print("[rip + 0x0] ", .{}); |
| 411 | const got_entry = print.mir.instructions.items(.data)[inst].got_entry; |
| 412 | if (print.bin_file.cast(link.File.MachO)) |macho_file| { |
| 413 | const target = macho_file.locals.items[got_entry]; |
| 414 | const target_name = macho_file.getString(target.n_strx); |
| 415 | try w.print("target@{s}", .{target_name}); |
| 416 | } else { |
| 417 | try w.writeAll("TODO lea reg, [rip + reloc] for linking backends different than MachO"); |
| 418 | } |
| 419 | }, |
| 420 | 0b11 => { |
| 421 | try w.writeAll("unused variant\n"); |
| 422 | }, |
| 423 | } |
| 424 | try w.writeAll("\n"); |
| 588 | 425 | } |
| 589 | 426 | |
| 590 | 427 | fn mirCallExtern(print: *const Print, inst: Mir.Inst.Index, w: anytype) !void { |