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
24022402 .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(zir.Inst.BitCast).?),
24032403 .elemptr => return self.analyzeInstElemPtr(scope, old_inst.cast(zir.Inst.ElemPtr).?),
24042404 .add => return self.analyzeInstAdd(scope, old_inst.cast(zir.Inst.Add).?),
2405 .sub => return self.analyzeInstSub(scope, old_inst.cast(zir.Inst.Sub).?),
24052406 .cmp => return self.analyzeInstCmp(scope, old_inst.cast(zir.Inst.Cmp).?),
24062407 .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?),
24072408 .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
29032904 return self.fail(scope, inst.base.src, "TODO implement more analyze elemptr", .{});
29042905}
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
29062911fn analyzeInstAdd(self: *Module, scope: *Scope, inst: *zir.Inst.Add) InnerError!*Inst {
29072912 const tracy = trace(@src());
29082913 defer tracy.end();
src-self-hosted/codegen.zig+206-91
......@@ -285,6 +285,9 @@ const Function = struct {
285285 memory: u64,
286286 /// The value is one of the stack variables.
287287 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
289292 fn isMemory(mcv: MCValue) bool {
290293 return switch (mcv) {
......@@ -292,6 +295,31 @@ const Function = struct {
292295 else => false,
293296 };
294297 }
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 }
295323 };
296324
297325 fn gen(self: *Function) !void {
......@@ -362,20 +390,21 @@ const Function = struct {
362390 switch (inst.tag) {
363391 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
364392 .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).?),
365395 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
366396 .breakpoint => return self.genBreakpoint(inst.src, arch),
367397 .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),
369400 .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),
371403 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
372 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
373404 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
374405 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
375 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
376 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
377 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
378 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
406 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
407 .unreach => return MCValue{ .unreach = {} },
379408 }
380409 }
381410
......@@ -385,96 +414,136 @@ const Function = struct {
385414 return MCValue.dead;
386415 switch (arch) {
387416 .x86_64 => {
388 // Biggest encoding of ADD is 8 bytes.
389 try self.code.ensureCapacity(self.code.items.len + 8);
417 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
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.
392 // Either one, but not both, can be a memory operand.
393 // Source operand can be an immediate, 8 bits or 32 bits.
394 // So, if either one of the operands dies with this instruction, we can use it
395 // as the result MCValue.
396 var dst_mcv: MCValue = undefined;
397 var src_mcv: MCValue = undefined;
398 if (inst.base.operandDies(0)) {
399 // LHS dies; use it as the destination.
400 dst_mcv = try self.resolveInst(inst.args.lhs);
401 // Both operands cannot be memory.
402 if (dst_mcv.isMemory()) {
403 src_mcv = try self.resolveInstImmOrReg(inst.args.rhs);
404 } else {
405 src_mcv = try self.resolveInst(inst.args.rhs);
406 }
407 } else if (inst.base.operandDies(1)) {
408 // RHS dies; use it as the destination.
409 dst_mcv = try self.resolveInst(inst.args.rhs);
410 // Both operands cannot be memory.
411 if (dst_mcv.isMemory()) {
412 src_mcv = try self.resolveInstImmOrReg(inst.args.lhs);
413 } else {
414 src_mcv = try self.resolveInst(inst.args.lhs);
415 }
416 } else {
417 const lhs = try self.resolveInst(inst.args.lhs);
418 const rhs = try self.resolveInst(inst.args.rhs);
419 if (lhs.isMemory()) {
420 dst_mcv = try self.copyToNewRegister(inst.base.src, lhs);
421 src_mcv = rhs;
422 } else {
423 dst_mcv = try self.copyToNewRegister(inst.base.src, rhs);
424 src_mcv = lhs;
425 }
426 }
427 // x86 ADD supports only signed 32-bit immediates at most. If the immediate
428 // value is larger than this, we put it in a register.
429 // A potential opportunity for future optimization here would be keeping track
430 // of the fact that the instruction is available both as an immediate
431 // and as a register.
432 switch (src_mcv) {
433 .immediate => |imm| {
434 if (imm > std.math.maxInt(u31)) {
435 src_mcv = try self.copyToNewRegister(inst.base.src, src_mcv);
436 }
437 },
438 else => {},
423 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {
424 // No side effects, so if it's unreferenced, do nothing.
425 if (inst.base.isUnused())
426 return MCValue.dead;
427 switch (arch) {
428 .x86_64 => {
429 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
430 },
431 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
432 }
433 }
434
435 /// ADD, SUB
436 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
437 try self.code.ensureCapacity(self.code.items.len + 8);
438
439 const lhs = try self.resolveInst(op_lhs);
440 const rhs = try self.resolveInst(op_rhs);
441
442 // There are 2 operands, destination and source.
443 // Either one, but not both, can be a memory operand.
444 // Source operand can be an immediate, 8 bits or 32 bits.
445 // So, if either one of the operands dies with this instruction, we can use it
446 // as the result MCValue.
447 var dst_mcv: MCValue = undefined;
448 var src_mcv: MCValue = undefined;
449 var src_inst: *ir.Inst = undefined;
450 if (inst.operandDies(0) and lhs.isMutable()) {
451 // LHS dies; use it as the destination.
452 // Both operands cannot be memory.
453 src_inst = op_rhs;
454 if (lhs.isMemory() and rhs.isMemory()) {
455 dst_mcv = try self.copyToNewRegister(op_lhs);
456 src_mcv = rhs;
457 } else {
458 dst_mcv = lhs;
459 src_mcv = rhs;
460 }
461 } else if (inst.operandDies(1) and rhs.isMutable()) {
462 // RHS dies; use it as the destination.
463 // Both operands cannot be memory.
464 src_inst = op_lhs;
465 if (lhs.isMemory() and rhs.isMemory()) {
466 dst_mcv = try self.copyToNewRegister(op_rhs);
467 src_mcv = lhs;
468 } else {
469 dst_mcv = rhs;
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);
439492 }
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) {
442510 .none => unreachable,
443 .dead, .unreach, .immediate => unreachable,
444 .register => |dst_reg_usize| {
445 const dst_reg = @intToEnum(Reg(arch), @intCast(@TagType(Reg(arch)), dst_reg_usize));
446 switch (src_mcv) {
447 .none => unreachable,
448 .dead, .unreach => unreachable,
449 .register => |src_reg_usize| {
450 const src_reg = @intToEnum(Reg(arch), @intCast(@TagType(Reg(arch)), src_reg_usize));
451 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
452 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
453 },
454 .immediate => |imm| {
455 const imm32 = @intCast(u31, imm); // We handle this case above.
456 // 81 /0 id
457 if (imm32 <= std.math.maxInt(u7)) {
458 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
459 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x83, 0xC0 | @as(u8, dst_reg.id() & 0b111), @intCast(u8, imm32)});
460 } else {
461 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
462 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x81, 0xC0 | @as(u8, dst_reg.id() & 0b111) });
463 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
464 }
465 },
466 .embedded_in_code, .memory, .stack_offset => {
467 return self.fail(inst.base.src, "TODO implement x86 add source memory", .{});
468 },
511 .dead, .unreach => unreachable,
512 .register => |src_reg_usize| {
513 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
514 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
515 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
516 },
517 .immediate => |imm| {
518 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
519 // 81 /opx id
520 if (imm32 <= std.math.maxInt(u7)) {
521 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
522 self.code.appendSliceAssumeCapacity(&[_]u8{
523 0x83,
524 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
525 @intCast(u8, imm32),
526 });
527 } else {
528 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
529 self.code.appendSliceAssumeCapacity(&[_]u8{
530 0x81,
531 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
532 });
533 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
469534 }
470535 },
471536 .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", .{});
473541 },
474542 }
475 return dst_mcv;
476543 },
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 },
478547 }
479548 }
480549
......@@ -550,7 +619,29 @@ const Function = struct {
550619 }
551620
552621 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;
553625 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 },
554645 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
555646 }
556647 }
......@@ -668,6 +759,9 @@ const Function = struct {
668759 .dead => unreachable,
669760 .none => unreachable,
670761 .unreach => unreachable,
762 .compare_flag => |op| {
763 return self.fail(src, "TODO set register with compare flag value", .{});
764 },
671765 .immediate => |x| {
672766 if (reg.size() != 64) {
673767 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
......@@ -871,14 +965,35 @@ const Function = struct {
871965 }
872966 }
873967
874 fn resolveInstImmOrReg(self: *Function, inst: *ir.Inst) !MCValue {
875 return self.fail(inst.src, "TODO implement resolveInstImmOrReg", .{});
968 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
969 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
876970 }
877971
878 fn copyToNewRegister(self: *Function, src: usize, mcv: MCValue) !MCValue {
879 return self.fail(src, "TODO implement copyToNewRegister", .{});
972 /// If the MCValue is an immediate, and it does not fit within this type,
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;
880994 }
881995
996
882997 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
883998 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
884999 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
src-self-hosted/ir.zig+13-1
......@@ -51,6 +51,7 @@ pub const Inst = struct {
5151 ptrtoint,
5252 ret,
5353 retvoid,
54 sub,
5455 unreach,
5556
5657 /// Returns whether the instruction is one of the control flow "noreturn" types.
......@@ -64,12 +65,13 @@ pub const Inst = struct {
6465 .bitcast,
6566 .block,
6667 .breakpoint,
68 .call,
6769 .cmp,
6870 .constant,
6971 .isnonnull,
7072 .isnull,
7173 .ptrtoint,
72 .call,
74 .sub,
7375 => false,
7476
7577 .condbr,
......@@ -242,6 +244,16 @@ pub const Inst = struct {
242244 args: void,
243245 };
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
245257 pub const Unreach = struct {
246258 pub const base_tag = Tag.unreach;
247259 base: Inst,
src-self-hosted/zir.zig+30
......@@ -73,6 +73,7 @@ pub const Inst = struct {
7373 bitcast,
7474 elemptr,
7575 add,
76 sub,
7677 cmp,
7778 condbr,
7879 isnull,
......@@ -110,6 +111,7 @@ pub const Inst = struct {
110111 .bitcast => BitCast,
111112 .elemptr => ElemPtr,
112113 .add => Add,
114 .sub => Sub,
113115 .cmp => Cmp,
114116 .condbr => CondBr,
115117 .isnull => IsNull,
......@@ -512,6 +514,17 @@ pub const Inst = struct {
512514 kw_args: struct {},
513515 };
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
515528 pub const Cmp = struct {
516529 pub const base_tag = Tag.cmp;
517530 base: Inst,
......@@ -677,6 +690,7 @@ pub const Module = struct {
677690 .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, inst, inst_table),
678691 .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, inst, inst_table),
679692 .add => return self.writeInstToStreamGeneric(stream, .add, inst, inst_table),
693 .sub => return self.writeInstToStreamGeneric(stream, .sub, inst, inst_table),
680694 .cmp => return self.writeInstToStreamGeneric(stream, .cmp, inst, inst_table),
681695 .condbr => return self.writeInstToStreamGeneric(stream, .condbr, inst, inst_table),
682696 .isnull => return self.writeInstToStreamGeneric(stream, .isnull, inst, inst_table),
......@@ -1542,6 +1556,22 @@ const EmitZIR = struct {
15421556 };
15431557 break :blk &new_inst.base;
15441558 },
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 },
15451575 .arg => blk: {
15461576 const old_inst = inst.cast(ir.Inst.Arg).?;
15471577 const new_inst = try self.arena.allocator.create(Inst.Arg);