authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-06 09:21:57+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-06 09:21:57+00:00
log12737c9a3071a49f8c05348fadd0b602b6952542
tree3342c1945bc0ef57393b64a1a4ee94317ad0f6fb
parent8be8ebd698aac447db2babf95def4725d9ddd05f

stage2: codegen skeleton for cmp and sub


4 files changed, 254 insertions(+), 92 deletions(-)

src-self-hosted/Module.zig+5
...@@ -2402,6 +2402,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In...@@ -2402,6 +2402,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
2402 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),2402 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
2403 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),2403 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
2404 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),2404 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
2405 .sub => return self.analyzeInstSub(scope, old_inst.cast(zir.Inst.Sub).?),
2405 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),2406 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
2406 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),2407 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
2407 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),2408 .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?),
...@@ -2903,6 +2904,10 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn...@@ -2903,6 +2904,10 @@ fn analyzeInstElemPtr(self: *Module, scope: *Scope, inst: *zir.Inst.ElemPtr) Inn
2903 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});2904 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
2904}2905}
29052906
2907fn analyzeInstSub(self: *Module, scope: *Scope, inst: *zir.Inst.Sub) InnerError!*Inst {
2908 return self.fail(scope, inst.base.src, "TODO implement analysis of sub", .{});
2909}
2910
2906fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {2911fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
2907 const tracy = trace(@src());2912 const tracy = trace(@src());
2908 defer tracy.end();2913 defer tracy.end();
src-self-hosted/codegen.zig+206-91
...@@ -285,6 +285,9 @@ const Function = struct {...@@ -285,6 +285,9 @@ const Function = struct {
285 memory: u64,285 memory: u64,
286 /// The value is one of the stack variables.286 /// The value is one of the stack variables.
287 stack_offset: u64,287 stack_offset: u64,
288 /// The value is the compare flag, with this operator
289 /// applied on top of it.
290 compare_flag: std.math.CompareOperator,
288291
289 fn isMemory(mcv: MCValue) bool {292 fn isMemory(mcv: MCValue) bool {
290 return switch (mcv) {293 return switch (mcv) {
...@@ -292,6 +295,31 @@ const Function = struct {...@@ -292,6 +295,31 @@ const Function = struct {
292 else => false,295 else => false,
293 };296 };
294 }297 }
298
299 fn isImmediate(mcv: MCValue) bool {
300 return switch (mcv) {
301 .immediate => true,
302 else => false,
303 };
304 }
305
306 fn isMutable(mcv: MCValue) bool {
307 return switch (mcv) {
308 .none => unreachable,
309 .unreach => unreachable,
310 .dead => unreachable,
311
312 .immediate,
313 .embedded_in_code,
314 .memory,
315 .compare_flag,
316 => false,
317
318 .register,
319 .stack_offset,
320 => true,
321 };
322 }
295 };323 };
296324
297 fn gen(self: *Function) !void {325 fn gen(self: *Function) !void {
...@@ -362,20 +390,21 @@ const Function = struct {...@@ -362,20 +390,21 @@ const Function = struct {
362 switch (inst.tag) {390 switch (inst.tag) {
363 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),391 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
364 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),392 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
393 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
394 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
365 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),395 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
366 .breakpoint => return self.genBreakpoint(inst.src, arch),396 .breakpoint => return self.genBreakpoint(inst.src, arch),
367 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),397 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
368 .unreach => return MCValue{ .unreach = {} },398 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
399 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
369 .constant => unreachable, // excluded from function bodies400 .constant => unreachable, // excluded from function bodies
370 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),401 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
402 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
371 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),403 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
372 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
373 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),404 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
374 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),405 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
375 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),406 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
376 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),407 .unreach => return MCValue{ .unreach = {} },
377 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
378 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
379 }408 }
380 }409 }
381410
...@@ -385,96 +414,136 @@ const Function = struct {...@@ -385,96 +414,136 @@ const Function = struct {
385 return MCValue.dead;414 return MCValue.dead;
386 switch (arch) {415 switch (arch) {
387 .x86_64 => {416 .x86_64 => {
388 // Biggest encoding of ADD is 8 bytes.417 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
389 try self.code.ensureCapacity(self.code.items.len + 8);418 },
419 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
420 }
421 }
390422
391 // In x86, ADD has 2 operands, destination and source.423 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {
392 // Either one, but not both, can be a memory operand.424 // No side effects, so if it's unreferenced, do nothing.
393 // Source operand can be an immediate, 8 bits or 32 bits.425 if (inst.base.isUnused())
394 // So, if either one of the operands dies with this instruction, we can use it426 return MCValue.dead;
395 // as the result MCValue.427 switch (arch) {
396 var dst_mcv: MCValue = undefined;428 .x86_64 => {
397 var src_mcv: MCValue = undefined;429 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
398 if (inst.base.operandDies(0)) {430 },
399 // LHS dies; use it as the destination.431 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
400 dst_mcv = try self.resolveInst(inst.args.lhs);432 }
401 // Both operands cannot be memory.433 }
402 if (dst_mcv.isMemory()) {434
403 src_mcv = try self.resolveInstImmOrReg(inst.args.rhs);435 /// ADD, SUB
404 } else {436 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
405 src_mcv = try self.resolveInst(inst.args.rhs);437 try self.code.ensureCapacity(self.code.items.len + 8);
406 }438
407 } else if (inst.base.operandDies(1)) {439 const lhs = try self.resolveInst(op_lhs);
408 // RHS dies; use it as the destination.440 const rhs = try self.resolveInst(op_rhs);
409 dst_mcv = try self.resolveInst(inst.args.rhs);441
410 // Both operands cannot be memory.442 // There are 2 operands, destination and source.
411 if (dst_mcv.isMemory()) {443 // Either one, but not both, can be a memory operand.
412 src_mcv = try self.resolveInstImmOrReg(inst.args.lhs);444 // Source operand can be an immediate, 8 bits or 32 bits.
413 } else {445 // So, if either one of the operands dies with this instruction, we can use it
414 src_mcv = try self.resolveInst(inst.args.lhs);446 // as the result MCValue.
415 }447 var dst_mcv: MCValue = undefined;
416 } else {448 var src_mcv: MCValue = undefined;
417 const lhs = try self.resolveInst(inst.args.lhs);449 var src_inst: *ir.Inst = undefined;
418 const rhs = try self.resolveInst(inst.args.rhs);450 if (inst.operandDies(0) and lhs.isMutable()) {
419 if (lhs.isMemory()) {451 // LHS dies; use it as the destination.
420 dst_mcv = try self.copyToNewRegister(inst.base.src, lhs);452 // Both operands cannot be memory.
421 src_mcv = rhs;453 src_inst = op_rhs;
422 } else {454 if (lhs.isMemory() and rhs.isMemory()) {
423 dst_mcv = try self.copyToNewRegister(inst.base.src, rhs);455 dst_mcv = try self.copyToNewRegister(op_lhs);
424 src_mcv = lhs;456 src_mcv = rhs;
425 }457 } else {
426 }458 dst_mcv = lhs;
427 // x86 ADD supports only signed 32-bit immediates at most. If the immediate459 src_mcv = rhs;
428 // value is larger than this, we put it in a register.460 }
429 // A potential opportunity for future optimization here would be keeping track461 } else if (inst.operandDies(1) and rhs.isMutable()) {
430 // of the fact that the instruction is available both as an immediate462 // RHS dies; use it as the destination.
431 // and as a register.463 // Both operands cannot be memory.
432 switch (src_mcv) {464 src_inst = op_lhs;
433 .immediate => |imm| {465 if (lhs.isMemory() and rhs.isMemory()) {
434 if (imm > std.math.maxInt(u31)) {466 dst_mcv = try self.copyToNewRegister(op_rhs);
435 src_mcv = try self.copyToNewRegister(inst.base.src, src_mcv);467 src_mcv = lhs;
436 }468 } else {
437 },469 dst_mcv = rhs;
438 else => {},470 src_mcv = lhs;
471 }
472 } else {
473 if (lhs.isMemory()) {
474 dst_mcv = try self.copyToNewRegister(op_lhs);
475 src_mcv = rhs;
476 src_inst = op_rhs;
477 } else {
478 dst_mcv = try self.copyToNewRegister(op_rhs);
479 src_mcv = lhs;
480 src_inst = op_lhs;
481 }
482 }
483 // This instruction supports only signed 32-bit immediates at most. If the immediate
484 // value is larger than this, we put it in a register.
485 // A potential opportunity for future optimization here would be keeping track
486 // of the fact that the instruction is available both as an immediate
487 // and as a register.
488 switch (src_mcv) {
489 .immediate => |imm| {
490 if (imm > std.math.maxInt(u31)) {
491 src_mcv = try self.copyToNewRegister(src_inst);
439 }492 }
493 },
494 else => {},
495 }
496
497 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
440498
441 switch (dst_mcv) {499 return dst_mcv;
500 }
501
502 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
503 switch (dst_mcv) {
504 .none => unreachable,
505 .dead, .unreach, .immediate => unreachable,
506 .compare_flag => unreachable,
507 .register => |dst_reg_usize| {
508 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
509 switch (src_mcv) {
442 .none => unreachable,510 .none => unreachable,
443 .dead, .unreach, .immediate => unreachable,511 .dead, .unreach => unreachable,
444 .register => |dst_reg_usize| {512 .register => |src_reg_usize| {
445 const dst_reg = @intToEnum(Reg(arch), @intCast(@TagType(Reg(arch)), dst_reg_usize));513 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
446 switch (src_mcv) {514 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
447 .none => unreachable,515 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
448 .dead, .unreach => unreachable,516 },
449 .register => |src_reg_usize| {517 .immediate => |imm| {
450 const src_reg = @intToEnum(Reg(arch), @intCast(@TagType(Reg(arch)), src_reg_usize));518 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
451 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });519 // 81 /opx id
452 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });520 if (imm32 <= std.math.maxInt(u7)) {
453 },521 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
454 .immediate => |imm| {522 self.code.appendSliceAssumeCapacity(&[_]u8{
455 const imm32 = @intCast(u31, imm); // We handle this case above.523 0x83,
456 // 81 /0 id524 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
457 if (imm32 <= std.math.maxInt(u7)) {525 @intCast(u8, imm32),
458 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });526 });
459 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x83, 0xC0 | @as(u8, dst_reg.id() & 0b111), @intCast(u8, imm32)});527 } else {
460 } else {528 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
461 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });529 self.code.appendSliceAssumeCapacity(&[_]u8{
462 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x81, 0xC0 | @as(u8, dst_reg.id() & 0b111) });530 0x81,
463 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);531 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
464 }532 });
465 },533 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
466 .embedded_in_code, .memory, .stack_offset => {
467 return self.fail(inst.base.src, "TODO implement x86 add source memory", .{});
468 },
469 }534 }
470 },535 },
471 .embedded_in_code, .memory, .stack_offset => {536 .embedded_in_code, .memory, .stack_offset => {
472 return self.fail(inst.base.src, "TODO implement x86 add destination memory", .{});537 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
538 },
539 .compare_flag => {
540 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag", .{});
473 },541 },
474 }542 }
475 return dst_mcv;
476 },543 },
477 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),544 .embedded_in_code, .memory, .stack_offset => {
545 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
546 },
478 }547 }
479 }548 }
480549
...@@ -550,7 +619,29 @@ const Function = struct {...@@ -550,7 +619,29 @@ const Function = struct {
550 }619 }
551620
552 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {621 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
622 // No side effects, so if it's unreferenced, do nothing.
623 if (inst.base.isUnused())
624 return MCValue.dead;
553 switch (arch) {625 switch (arch) {
626 .x86_64 => {
627 try self.code.ensureCapacity(self.code.items.len + 8);
628
629 const lhs = try self.resolveInst(inst.args.lhs);
630 const rhs = try self.resolveInst(inst.args.rhs);
631
632 // There are 2 operands, destination and source.
633 // Either one, but not both, can be a memory operand.
634 // Source operand can be an immediate, 8 bits or 32 bits.
635 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
636 try self.copyToNewRegister(inst.args.lhs)
637 else
638 lhs;
639 // This instruction supports only signed 32-bit immediates at most.
640 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
641
642 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
643 return MCValue{.compare_flag = inst.args.op};
644 },
554 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),645 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
555 }646 }
556 }647 }
...@@ -668,6 +759,9 @@ const Function = struct {...@@ -668,6 +759,9 @@ const Function = struct {
668 .dead => unreachable,759 .dead => unreachable,
669 .none => unreachable,760 .none => unreachable,
670 .unreach => unreachable,761 .unreach => unreachable,
762 .compare_flag => |op| {
763 return self.fail(src, "TODO set register with compare flag value", .{});
764 },
671 .immediate => |x| {765 .immediate => |x| {
672 if (reg.size() != 64) {766 if (reg.size() != 64) {
673 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});767 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
...@@ -871,14 +965,35 @@ const Function = struct {...@@ -871,14 +965,35 @@ const Function = struct {
871 }965 }
872 }966 }
873967
874 fn resolveInstImmOrReg(self: *Function, inst: *ir.Inst) !MCValue {968 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
875 return self.fail(inst.src, "TODO implement resolveInstImmOrReg", .{});969 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
876 }970 }
877971
878 fn copyToNewRegister(self: *Function, src: usize, mcv: MCValue) !MCValue {972 /// If the MCValue is an immediate, and it does not fit within this type,
879 return self.fail(src, "TODO implement copyToNewRegister", .{});973 /// we put it in a register.
974 /// A potential opportunity for future optimization here would be keeping track
975 /// of the fact that the instruction is available both as an immediate
976 /// and as a register.
977 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {
978 const mcv = try self.resolveInst(inst);
979 const ti = @typeInfo(T).Int;
980 switch (mcv) {
981 .immediate => |imm| {
982 // This immediate is unsigned.
983 const U = @Type(.{ .Int = .{
984 .bits = ti.bits - @boolToInt(ti.is_signed),
985 .is_signed = false,
986 }});
987 if (imm >= std.math.maxInt(U)) {
988 return self.copyToNewRegister(inst);
989 }
990 },
991 else => {},
992 }
993 return mcv;
880 }994 }
881995
996
882 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {997 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
883 const ptr_bits = self.target.cpu.arch.ptrBitWidth();998 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
884 const ptr_bytes: u64 = @divExact(ptr_bits, 8);999 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
src-self-hosted/ir.zig+13-1
...@@ -51,6 +51,7 @@ pub const Inst = struct {...@@ -51,6 +51,7 @@ pub const Inst = struct {
51 ptrtoint,51 ptrtoint,
52 ret,52 ret,
53 retvoid,53 retvoid,
54 sub,
54 unreach,55 unreach,
5556
56 /// Returns whether the instruction is one of the control flow "noreturn" types.57 /// Returns whether the instruction is one of the control flow "noreturn" types.
...@@ -64,12 +65,13 @@ pub const Inst = struct {...@@ -64,12 +65,13 @@ pub const Inst = struct {
64 .bitcast,65 .bitcast,
65 .block,66 .block,
66 .breakpoint,67 .breakpoint,
68 .call,
67 .cmp,69 .cmp,
68 .constant,70 .constant,
69 .isnonnull,71 .isnonnull,
70 .isnull,72 .isnull,
71 .ptrtoint,73 .ptrtoint,
72 .call,74 .sub,
73 => false,75 => false,
7476
75 .condbr,77 .condbr,
...@@ -242,6 +244,16 @@ pub const Inst = struct {...@@ -242,6 +244,16 @@ pub const Inst = struct {
242 args: void,244 args: void,
243 };245 };
244246
247 pub const Sub = struct {
248 pub const base_tag = Tag.sub;
249 base: Inst,
250
251 args: struct {
252 lhs: *Inst,
253 rhs: *Inst,
254 },
255 };
256
245 pub const Unreach = struct {257 pub const Unreach = struct {
246 pub const base_tag = Tag.unreach;258 pub const base_tag = Tag.unreach;
247 base: Inst,259 base: Inst,
src-self-hosted/zir.zig+30
...@@ -73,6 +73,7 @@ pub const Inst = struct {...@@ -73,6 +73,7 @@ pub const Inst = struct {
73 bitcast,73 bitcast,
74 elemptr,74 elemptr,
75 add,75 add,
76 sub,
76 cmp,77 cmp,
77 condbr,78 condbr,
78 isnull,79 isnull,
...@@ -110,6 +111,7 @@ pub const Inst = struct {...@@ -110,6 +111,7 @@ pub const Inst = struct {
110 .bitcast => BitCast,111 .bitcast => BitCast,
111 .elemptr => ElemPtr,112 .elemptr => ElemPtr,
112 .add => Add,113 .add => Add,
114 .sub => Sub,
113 .cmp => Cmp,115 .cmp => Cmp,
114 .condbr => CondBr,116 .condbr => CondBr,
115 .isnull => IsNull,117 .isnull => IsNull,
...@@ -512,6 +514,17 @@ pub const Inst = struct {...@@ -512,6 +514,17 @@ pub const Inst = struct {
512 kw_args: struct {},514 kw_args: struct {},
513 };515 };
514516
517 pub const Sub = struct {
518 pub const base_tag = Tag.sub;
519 base: Inst,
520
521 positionals: struct {
522 lhs: *Inst,
523 rhs: *Inst,
524 },
525 kw_args: struct {},
526 };
527
515 pub const Cmp = struct {528 pub const Cmp = struct {
516 pub const base_tag = Tag.cmp;529 pub const base_tag = Tag.cmp;
517 base: Inst,530 base: Inst,
...@@ -677,6 +690,7 @@ pub const Module = struct {...@@ -677,6 +690,7 @@ pub const Module = struct {
677 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table),690 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table),
678 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table),691 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table),
679 .add => return self.writeInstToStreamGeneric(stream, .add, inst, inst_table),692 .add => return self.writeInstToStreamGeneric(stream, .add, inst, inst_table),
693 .sub => return self.writeInstToStreamGeneric(stream, .sub, inst, inst_table),
680 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst, inst_table),694 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst, inst_table),
681 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst, inst_table),695 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst, inst_table),
682 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst, inst_table),696 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst, inst_table),
...@@ -1542,6 +1556,22 @@ const EmitZIR = struct {...@@ -1542,6 +1556,22 @@ const EmitZIR = struct {
1542 };1556 };
1543 break :blk &new_inst.base;1557 break :blk &new_inst.base;
1544 },1558 },
1559 .sub => blk: {
1560 const old_inst = inst.cast(ir.Inst.Sub).?;
1561 const new_inst = try self.arena.allocator.create(Inst.Sub);
1562 new_inst.* = .{
1563 .base = .{
1564 .src = inst.src,
1565 .tag = Inst.Sub.base_tag,
1566 },
1567 .positionals = .{
1568 .lhs = try self.resolveInst(new_body, old_inst.args.lhs),
1569 .rhs = try self.resolveInst(new_body, old_inst.args.rhs),
1570 },
1571 .kw_args = .{},
1572 };
1573 break :blk &new_inst.base;
1574 },
1545 .arg => blk: {1575 .arg => blk: {
1546 const old_inst = inst.cast(ir.Inst.Arg).?;1576 const old_inst = inst.cast(ir.Inst.Arg).?;
1547 const new_inst = try self.arena.allocator.create(Inst.Arg);1577 const new_inst = try self.arena.allocator.create(Inst.Arg);