authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 02:29:46-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 02:29:46-07:00
logb3b00ec62f518875a486b4da532f74e304c3aba2
tree5ae6f6dd8afba7cacd3d5b1532d4c25b3661189d
parent4fdfaf69c8c55ebac4c5b3c00025c0ac51281b5b
parent8899e6e334758f2e101399075d0456195035c372

Merge branch 'more-stage2-locals'


12 files changed, 916 insertions(+), 238 deletions(-)

lib/std/fmt.zig+2
...@@ -88,6 +88,8 @@ pub fn format(...@@ -88,6 +88,8 @@ pub fn format(
88 if (args.len > ArgSetType.bit_count) {88 if (args.len > ArgSetType.bit_count) {
89 @compileError("32 arguments max are supported per format call");89 @compileError("32 arguments max are supported per format call");
90 }90 }
91 if (args.len == 0)
92 return writer.writeAll(fmt);
9193
92 const State = enum {94 const State = enum {
93 Start,95 Start,
src-self-hosted/Module.zig+66-19
...@@ -47,7 +47,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},...@@ -47,7 +47,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
47/// Maps fully qualified namespaced names to the Decl struct for them.47/// Maps fully qualified namespaced names to the Decl struct for them.
48decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},48decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
4949
50optimize_mode: std.builtin.Mode,
51link_error_flags: link.File.ErrorFlags = .{},50link_error_flags: link.File.ErrorFlags = .{},
5251
53work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),52work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
...@@ -385,18 +384,6 @@ pub const Scope = struct {...@@ -385,18 +384,6 @@ pub const Scope = struct {
385 };384 };
386 }385 }
387386
388 pub fn dumpInst(self: *Scope, inst: *Inst) void {
389 const zir_module = self.namespace();
390 const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
391 std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
392 zir_module.sub_file_path,
393 loc.line + 1,
394 loc.column + 1,
395 @tagName(inst.tag),
396 inst.ty,
397 });
398 }
399
400 /// Asserts the scope has a parent which is a ZIRModule or File and387 /// Asserts the scope has a parent which is a ZIRModule or File and
401 /// returns the sub_file_path field.388 /// returns the sub_file_path field.
402 pub fn subFilePath(base: *Scope) []const u8 {389 pub fn subFilePath(base: *Scope) []const u8 {
...@@ -802,6 +789,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -802,6 +789,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
802 .output_mode = options.output_mode,789 .output_mode = options.output_mode,
803 .link_mode = options.link_mode orelse .Static,790 .link_mode = options.link_mode orelse .Static,
804 .object_format = options.object_format orelse options.target.getObjectFormat(),791 .object_format = options.object_format orelse options.target.getObjectFormat(),
792 .optimize_mode = options.optimize_mode,
805 });793 });
806 errdefer bin_file.destroy();794 errdefer bin_file.destroy();
807795
...@@ -838,7 +826,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {...@@ -838,7 +826,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
838 .bin_file_dir = bin_file_dir,826 .bin_file_dir = bin_file_dir,
839 .bin_file_path = options.bin_file_path,827 .bin_file_path = options.bin_file_path,
840 .bin_file = bin_file,828 .bin_file = bin_file,
841 .optimize_mode = options.optimize_mode,
842 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),829 .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
843 .keep_source_files_loaded = options.keep_source_files_loaded,830 .keep_source_files_loaded = options.keep_source_files_loaded,
844 };831 };
...@@ -894,7 +881,11 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {...@@ -894,7 +881,11 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
894}881}
895882
896pub fn target(self: Module) std.Target {883pub fn target(self: Module) std.Target {
897 return self.bin_file.options().target;884 return self.bin_file.options.target;
885}
886
887pub fn optimizeMode(self: Module) std.builtin.Mode {
888 return self.bin_file.options.optimize_mode;
898}889}
899890
900/// Detect changes to source files, perform semantic analysis, and update the output files.891/// Detect changes to source files, perform semantic analysis, and update the output files.
...@@ -1991,14 +1982,14 @@ pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {...@@ -1991,14 +1982,14 @@ pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst {
1991pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {1982pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
1992 return self.constInst(scope, src, .{1983 return self.constInst(scope, src, .{
1993 .ty = Type.initTag(.void),1984 .ty = Type.initTag(.void),
1994 .val = Value.initTag(.the_one_possible_value),1985 .val = Value.initTag(.void_value),
1995 });1986 });
1996}1987}
19971988
1998pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {1989pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
1999 return self.constInst(scope, src, .{1990 return self.constInst(scope, src, .{
2000 .ty = Type.initTag(.noreturn),1991 .ty = Type.initTag(.noreturn),
2001 .val = Value.initTag(.the_one_possible_value),1992 .val = Value.initTag(.unreachable_value),
2002 });1993 });
2003}1994}
20041995
...@@ -2151,7 +2142,8 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr...@@ -2151,7 +2142,8 @@ pub fn analyzeDeref(self: *Module, scope: *Scope, src: usize, ptr: *Inst, ptr_sr
2151 });2142 });
2152 }2143 }
21532144
2154 return self.fail(scope, src, "TODO implement runtime deref", .{});2145 const b = try self.requireRuntimeBlock(scope, src);
2146 return self.addUnOp(b, src, elem_ty, .load, ptr);
2155}2147}
21562148
2157pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {2149pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst {
...@@ -2161,7 +2153,8 @@ pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name:...@@ -2161,7 +2153,8 @@ pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name:
2161}2153}
21622154
2163pub fn wantSafety(self: *Module, scope: *Scope) bool {2155pub fn wantSafety(self: *Module, scope: *Scope) bool {
2164 return switch (self.optimize_mode) {2156 // TODO take into account scope's safety overrides
2157 return switch (self.optimizeMode()) {
2165 .Debug => true,2158 .Debug => true,
2166 .ReleaseSafe => true,2159 .ReleaseSafe => true,
2167 .ReleaseFast => false,2160 .ReleaseFast => false,
...@@ -2504,6 +2497,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2504,6 +2497,22 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2504 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });2497 return self.fail(scope, inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type });
2505}2498}
25062499
2500pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_value: *Inst) !*Inst {
2501 if (ptr.ty.isConstPtr())
2502 return self.fail(scope, src, "cannot assign to constant", .{});
2503
2504 const elem_ty = ptr.ty.elemType();
2505 const value = try self.coerce(scope, elem_ty, uncasted_value);
2506 if (elem_ty.onePossibleValue() != null)
2507 return self.constVoid(scope, src);
2508
2509 // TODO handle comptime pointer writes
2510 // TODO handle if the element type requires comptime
2511
2512 const b = try self.requireRuntimeBlock(scope, src);
2513 return self.addBinOp(b, src, Type.initTag(.void), .store, ptr, value);
2514}
2515
2507pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {2516pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2508 if (inst.value()) |val| {2517 if (inst.value()) |val| {
2509 // Keep the comptime Value representation; take the new type.2518 // Keep the comptime Value representation; take the new type.
...@@ -2780,3 +2789,41 @@ pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type)...@@ -2780,3 +2789,41 @@ pub fn singleMutPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type)
2780 type_payload.* = .{ .pointee_type = elem_ty };2789 type_payload.* = .{ .pointee_type = elem_ty };
2781 return Type.initPayload(&type_payload.base);2790 return Type.initPayload(&type_payload.base);
2782}2791}
2792
2793pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Type) error{OutOfMemory}!Type {
2794 const type_payload = try scope.arena().create(Type.Payload.SingleConstPointer);
2795 type_payload.* = .{ .pointee_type = elem_ty };
2796 return Type.initPayload(&type_payload.base);
2797}
2798
2799pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
2800 const zir_module = scope.namespace();
2801 const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
2802 const loc = std.zig.findLineColumn(source, inst.src);
2803 if (inst.tag == .constant) {
2804 std.debug.warn("constant ty={} val={} src={}:{}:{}\n", .{
2805 inst.ty,
2806 inst.castTag(.constant).?.val,
2807 zir_module.subFilePath(),
2808 loc.line + 1,
2809 loc.column + 1,
2810 });
2811 } else if (inst.deaths == 0) {
2812 std.debug.warn("{} ty={} src={}:{}:{}\n", .{
2813 @tagName(inst.tag),
2814 inst.ty,
2815 zir_module.subFilePath(),
2816 loc.line + 1,
2817 loc.column + 1,
2818 });
2819 } else {
2820 std.debug.warn("{} ty={} deaths={b} src={}:{}:{}\n", .{
2821 @tagName(inst.tag),
2822 inst.ty,
2823 inst.deaths,
2824 zir_module.subFilePath(),
2825 loc.line + 1,
2826 loc.column + 1,
2827 });
2828 }
2829}
src-self-hosted/astgen.zig+23-14
...@@ -87,7 +87,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr...@@ -87,7 +87,7 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
87 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),87 .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
88 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),88 .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
8989
90 .Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),90 .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
91 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),91 .Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
92 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),92 .StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
93 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),93 .IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
...@@ -145,8 +145,10 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block...@@ -145,8 +145,10 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
145145
146 else => {146 else => {
147 const possibly_unused_result = try expr(mod, scope, .none, statement);147 const possibly_unused_result = try expr(mod, scope, .none, statement);
148 const src = scope.tree().token_locs[statement.firstToken()].start;148 if (!possibly_unused_result.tag.isNoReturn()) {
149 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);149 const src = scope.tree().token_locs[statement.firstToken()].start;
150 _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
151 }
150 },152 },
151 }153 }
152 }154 }
...@@ -469,7 +471,7 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE...@@ -469,7 +471,7 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
469 }471 }
470}472}
471473
472fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst {474fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
473 const tracy = trace(@src());475 const tracy = trace(@src());
474 defer tracy.end();476 defer tracy.end();
475477
...@@ -481,7 +483,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -481,7 +483,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
481 }483 }
482484
483 if (getSimplePrimitiveValue(ident_name)) |typed_value| {485 if (getSimplePrimitiveValue(ident_name)) |typed_value| {
484 return addZIRInstConst(mod, scope, src, typed_value);486 const result = try addZIRInstConst(mod, scope, src, typed_value);
487 return rlWrap(mod, scope, rl, result);
485 }488 }
486489
487 if (ident_name.len >= 2) integer: {490 if (ident_name.len >= 2) integer: {
...@@ -505,16 +508,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -505,16 +508,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
505 else => {508 else => {
506 const int_type_payload = try scope.arena().create(Value.Payload.IntType);509 const int_type_payload = try scope.arena().create(Value.Payload.IntType);
507 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };510 int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
508 return addZIRInstConst(mod, scope, src, .{511 const result = try addZIRInstConst(mod, scope, src, .{
509 .ty = Type.initTag(.comptime_int),512 .ty = Type.initTag(.comptime_int),
510 .val = Value.initPayload(&int_type_payload.base),513 .val = Value.initPayload(&int_type_payload.base),
511 });514 });
515 return rlWrap(mod, scope, rl, result);
512 },516 },
513 };517 };
514 return addZIRInstConst(mod, scope, src, .{518 const result = try addZIRInstConst(mod, scope, src, .{
515 .ty = Type.initTag(.type),519 .ty = Type.initTag(.type),
516 .val = val,520 .val = val,
517 });521 });
522 return rlWrap(mod, scope, rl, result);
518 }523 }
519 }524 }
520525
...@@ -525,14 +530,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -525,14 +530,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
525 .local_val => {530 .local_val => {
526 const local_val = s.cast(Scope.LocalVal).?;531 const local_val = s.cast(Scope.LocalVal).?;
527 if (mem.eql(u8, local_val.name, ident_name)) {532 if (mem.eql(u8, local_val.name, ident_name)) {
528 return local_val.inst;533 return rlWrap(mod, scope, rl, local_val.inst);
529 }534 }
530 s = local_val.parent;535 s = local_val.parent;
531 },536 },
532 .local_ptr => {537 .local_ptr => {
533 const local_ptr = s.cast(Scope.LocalPtr).?;538 const local_ptr = s.cast(Scope.LocalPtr).?;
534 if (mem.eql(u8, local_ptr.name, ident_name)) {539 if (mem.eql(u8, local_ptr.name, ident_name)) {
535 return try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);540 if (rl == .lvalue) {
541 return local_ptr.ptr;
542 } else {
543 const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
544 return rlWrap(mod, scope, rl, result);
545 }
536 }546 }
537 s = local_ptr.parent;547 s = local_ptr.parent;
538 },548 },
...@@ -542,7 +552,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError...@@ -542,7 +552,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
542 }552 }
543553
544 if (mod.lookupDeclName(scope, ident_name)) |decl| {554 if (mod.lookupDeclName(scope, ident_name)) |decl| {
545 return try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});555 // TODO handle lvalues
556 const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
557 return rlWrap(mod, scope, rl, result);
546 }558 }
547559
548 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});560 return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
...@@ -1066,10 +1078,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr...@@ -1066,10 +1078,7 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
1066 .ptr = ptr_inst,1078 .ptr = ptr_inst,
1067 .value = result,1079 .value = result,
1068 }, .{});1080 }, .{});
1069 _ = try addZIRInst(mod, scope, result.src, zir.Inst.Store, .{1081 _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
1070 .ptr = ptr_inst,
1071 .value = casted_result,
1072 }, .{});
1073 return casted_result;1082 return casted_result;
1074 },1083 },
1075 .bitcasted_ptr => |bitcasted_ptr| {1084 .bitcasted_ptr => |bitcasted_ptr| {
src-self-hosted/codegen.zig+499-94
...@@ -50,7 +50,7 @@ pub fn generateSymbol(...@@ -50,7 +50,7 @@ pub fn generateSymbol(
5050
51 switch (typed_value.ty.zigTypeTag()) {51 switch (typed_value.ty.zigTypeTag()) {
52 .Fn => {52 .Fn => {
53 switch (bin_file.options.target.cpu.arch) {53 switch (bin_file.base.options.target.cpu.arch) {
54 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),54 //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
55 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),55 //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
56 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),56 //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
...@@ -143,7 +143,7 @@ pub fn generateSymbol(...@@ -143,7 +143,7 @@ pub fn generateSymbol(
143 // TODO handle the dependency of this symbol on the decl's vaddr.143 // TODO handle the dependency of this symbol on the decl's vaddr.
144 // If the decl changes vaddr, then this symbol needs to get regenerated.144 // If the decl changes vaddr, then this symbol needs to get regenerated.
145 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;145 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
146 const endian = bin_file.options.target.cpu.arch.endian();146 const endian = bin_file.base.options.target.cpu.arch.endian();
147 switch (bin_file.ptr_width) {147 switch (bin_file.ptr_width) {
148 .p32 => {148 .p32 => {
149 try code.resize(4);149 try code.resize(4);
...@@ -166,7 +166,7 @@ pub fn generateSymbol(...@@ -166,7 +166,7 @@ pub fn generateSymbol(
166 };166 };
167 },167 },
168 .Int => {168 .Int => {
169 const info = typed_value.ty.intInfo(bin_file.options.target);169 const info = typed_value.ty.intInfo(bin_file.base.options.target);
170 if (info.bits == 8 and !info.signed) {170 if (info.bits == 8 and !info.signed) {
171 const x = typed_value.val.toUnsignedInt();171 const x = typed_value.val.toUnsignedInt();
172 try code.append(@intCast(u8, x));172 try code.append(@intCast(u8, x));
...@@ -209,10 +209,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -209,10 +209,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
209 err_msg: ?*ErrorMsg,209 err_msg: ?*ErrorMsg,
210 args: []MCValue,210 args: []MCValue,
211 ret_mcv: MCValue,211 ret_mcv: MCValue,
212 fn_type: Type,
212 arg_index: usize,213 arg_index: usize,
213 src: usize,214 src: usize,
214 stack_align: u32,215 stack_align: u32,
215216
217 /// The value is an offset into the `Function` `code` from the beginning.
218 /// To perform the reloc, write 32-bit signed little-endian integer
219 /// which is a relative jump, based on the address following the reloc.
220 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
221
216 /// Whenever there is a runtime branch, we push a Branch onto this stack,222 /// Whenever there is a runtime branch, we push a Branch onto this stack,
217 /// and pop it off when the runtime branch joins. This provides an "overlay"223 /// and pop it off when the runtime branch joins. This provides an "overlay"
218 /// of the table of mappings from instructions to `MCValue` from within the branch.224 /// of the table of mappings from instructions to `MCValue` from within the branch.
...@@ -229,16 +235,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -229,16 +235,26 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
229 unreach,235 unreach,
230 /// No more references to this value remain.236 /// No more references to this value remain.
231 dead,237 dead,
238 /// The value is undefined.
239 undef,
232 /// A pointer-sized integer that fits in a register.240 /// A pointer-sized integer that fits in a register.
241 /// If the type is a pointer, this is the pointer address in virtual address space.
233 immediate: u64,242 immediate: u64,
234 /// The constant was emitted into the code, at this offset.243 /// The constant was emitted into the code, at this offset.
244 /// If the type is a pointer, it means the pointer address is embedded in the code.
235 embedded_in_code: usize,245 embedded_in_code: usize,
246 /// The value is a pointer to a constant which was emitted into the code, at this offset.
247 ptr_embedded_in_code: usize,
236 /// The value is in a target-specific register.248 /// The value is in a target-specific register.
237 register: Register,249 register: Register,
238 /// The value is in memory at a hard-coded address.250 /// The value is in memory at a hard-coded address.
251 /// If the type is a pointer, it means the pointer address is at this memory location.
239 memory: u64,252 memory: u64,
240 /// The value is one of the stack variables.253 /// The value is one of the stack variables.
241 stack_offset: u64,254 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
255 stack_offset: u32,
256 /// The value is a pointer to one of the stack variables (payload is stack offset).
257 ptr_stack_offset: u32,
242 /// The value is in the compare flags assuming an unsigned operation,258 /// The value is in the compare flags assuming an unsigned operation,
243 /// with this operator applied on top of it.259 /// with this operator applied on top of it.
244 compare_flags_unsigned: math.CompareOperator,260 compare_flags_unsigned: math.CompareOperator,
...@@ -271,6 +287,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -271,6 +287,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
271 .memory,287 .memory,
272 .compare_flags_unsigned,288 .compare_flags_unsigned,
273 .compare_flags_signed,289 .compare_flags_signed,
290 .ptr_stack_offset,
291 .ptr_embedded_in_code,
292 .undef,
274 => false,293 => false,
275294
276 .register,295 .register,
...@@ -309,6 +328,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -309,6 +328,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
309 self.free_registers |= @as(FreeRegInt, 1) << shift;328 self.free_registers |= @as(FreeRegInt, 1) << shift;
310 }329 }
311330
331 /// Before calling, must ensureCapacity + 1 on branch.registers.
332 /// Returns `null` if all registers are allocated.
333 fn allocReg(self: *Branch, inst: *ir.Inst) ?Register {
334 const free_index = @ctz(FreeRegInt, self.free_registers);
335 if (free_index >= callee_preserved_regs.len) {
336 return null;
337 }
338 self.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
339 const reg = callee_preserved_regs[free_index];
340 self.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
341 return reg;
342 }
343
312 fn deinit(self: *Branch, gpa: *Allocator) void {344 fn deinit(self: *Branch, gpa: *Allocator) void {
313 self.inst_table.deinit(gpa);345 self.inst_table.deinit(gpa);
314 self.registers.deinit(gpa);346 self.registers.deinit(gpa);
...@@ -349,18 +381,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -349,18 +381,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
349381
350 var function = Self{382 var function = Self{
351 .gpa = bin_file.allocator,383 .gpa = bin_file.allocator,
352 .target = &bin_file.options.target,384 .target = &bin_file.base.options.target,
353 .bin_file = bin_file,385 .bin_file = bin_file,
354 .mod_fn = module_fn,386 .mod_fn = module_fn,
355 .code = code,387 .code = code,
356 .err_msg = null,388 .err_msg = null,
357 .args = undefined, // populated after `resolveCallingConventionValues`389 .args = undefined, // populated after `resolveCallingConventionValues`
358 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`390 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
391 .fn_type = fn_type,
359 .arg_index = 0,392 .arg_index = 0,
360 .branch_stack = &branch_stack,393 .branch_stack = &branch_stack,
361 .src = src,394 .src = src,
362 .stack_align = undefined,395 .stack_align = undefined,
363 };396 };
397 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
364398
365 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {399 var call_info = function.resolveCallingConventionValues(src, fn_type) catch |err| switch (err) {
366 error.CodegenFail => return Result{ .fail = function.err_msg.? },400 error.CodegenFail => return Result{ .fail = function.err_msg.? },
...@@ -386,29 +420,78 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -386,29 +420,78 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
386 }420 }
387421
388 fn gen(self: *Self) !void {422 fn gen(self: *Self) !void {
389 try self.code.ensureCapacity(self.code.items.len + 11);423 switch (arch) {
390424 .x86_64 => {
391 // TODO omit this for naked functions425 try self.code.ensureCapacity(self.code.items.len + 11);
392 // push rbp426
393 // mov rbp, rsp427 const cc = self.fn_type.fnCallingConvention();
394 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });428 if (cc != .Naked) {
395429 // We want to subtract the aligned stack frame size from rsp here, but we don't
396 // sub rsp, x430 // yet know how big it will be, so we leave room for a 4-byte stack size.
397 const stack_end = self.branch_stack.items[0].max_end_stack;431 // TODO During semantic analysis, check if there are no function calls. If there
398 if (stack_end > math.maxInt(i32)) {432 // are none, here we can omit the part where we subtract and then add rsp.
399 return self.fail(self.src, "too much stack used in call parameters", .{});433 self.code.appendSliceAssumeCapacity(&[_]u8{
400 } else if (stack_end > math.maxInt(i8)) {434 // push rbp
401 // 48 83 ec xx sub rsp,0x10435 0x55,
402 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });436 // mov rbp, rsp
403 const x = @intCast(u32, stack_end);437 0x48,
404 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);438 0x89,
405 } else if (stack_end != 0) {439 0xe5,
406 // 48 81 ec xx xx xx xx sub rsp,0x80440 // sub rsp, imm32 (with reloc)
407 const x = @intCast(u8, stack_end);441 0x48,
408 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });442 0x81,
409 }443 0xec,
444 });
445 const reloc_index = self.code.items.len;
446 self.code.items.len += 4;
447
448 try self.genBody(self.mod_fn.analysis.success);
449
450 const stack_end = self.branch_stack.items[0].max_end_stack;
451 if (stack_end > math.maxInt(i32))
452 return self.fail(self.src, "too much stack used in call parameters", .{});
453 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
454 mem.writeIntLittle(u32, self.code.items[reloc_index..][0..4], @intCast(u32, aligned_stack_end));
455
456 if (self.code.items.len >= math.maxInt(i32)) {
457 return self.fail(self.src, "unable to perform relocation: jump too far", .{});
458 }
459 for (self.exitlude_jump_relocs.items) |jmp_reloc| {
460 const amt = self.code.items.len - (jmp_reloc + 4);
461 // If it wouldn't jump at all, elide it.
462 if (amt == 0) {
463 self.code.items.len -= 5;
464 continue;
465 }
466 const s32_amt = @intCast(i32, amt);
467 mem.writeIntLittle(i32, self.code.items[jmp_reloc..][0..4], s32_amt);
468 }
469
470 try self.code.ensureCapacity(self.code.items.len + 9);
471 // add rsp, x
472 if (aligned_stack_end > math.maxInt(i8)) {
473 // example: 48 81 c4 ff ff ff 7f add rsp,0x7fffffff
474 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xc4 });
475 const x = @intCast(u32, aligned_stack_end);
476 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
477 } else if (aligned_stack_end != 0) {
478 // example: 48 83 c4 7f add rsp,0x7f
479 const x = @intCast(u8, aligned_stack_end);
480 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xc4, x });
481 }
410482
411 try self.genBody(self.mod_fn.analysis.success);483 self.code.appendSliceAssumeCapacity(&[_]u8{
484 0x5d, // pop rbp
485 0xc3, // ret
486 });
487 } else {
488 try self.genBody(self.mod_fn.analysis.success);
489 }
490 },
491 else => {
492 try self.genBody(self.mod_fn.analysis.success);
493 },
494 }
412 }495 }
413496
414 fn genBody(self: *Self, body: ir.Body) InnerError!void {497 fn genBody(self: *Self, body: ir.Body) InnerError!void {
...@@ -432,8 +515,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -432,8 +515,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
432 entry.value = .dead;515 entry.value = .dead;
433 switch (prev_value) {516 switch (prev_value) {
434 .register => |reg| {517 .register => |reg| {
435 _ = branch.registers.remove(reg);518 const reg64 = reg.to64();
436 branch.markRegFree(reg);519 _ = branch.registers.remove(reg64);
520 branch.markRegFree(reg64);
437 },521 },
438 else => {}, // TODO process stack allocation death522 else => {}, // TODO process stack allocation death
439 }523 }
...@@ -459,26 +543,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -459,26 +543,23 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
459 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),543 .cmp_neq => return self.genCmp(inst.castTag(.cmp_neq).?, .neq),
460 .condbr => return self.genCondBr(inst.castTag(.condbr).?),544 .condbr => return self.genCondBr(inst.castTag(.condbr).?),
461 .constant => unreachable, // excluded from function bodies545 .constant => unreachable, // excluded from function bodies
546 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
547 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
462 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),548 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),
463 .isnull => return self.genIsNull(inst.castTag(.isnull).?),549 .isnull => return self.genIsNull(inst.castTag(.isnull).?),
550 .load => return self.genLoad(inst.castTag(.load).?),
551 .not => return self.genNot(inst.castTag(.not).?),
464 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),552 .ptrtoint => return self.genPtrToInt(inst.castTag(.ptrtoint).?),
553 .ref => return self.genRef(inst.castTag(.ref).?),
465 .ret => return self.genRet(inst.castTag(.ret).?),554 .ret => return self.genRet(inst.castTag(.ret).?),
466 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),555 .retvoid => return self.genRetVoid(inst.castTag(.retvoid).?),
556 .store => return self.genStore(inst.castTag(.store).?),
467 .sub => return self.genSub(inst.castTag(.sub).?),557 .sub => return self.genSub(inst.castTag(.sub).?),
468 .unreach => return MCValue{ .unreach = {} },558 .unreach => return MCValue{ .unreach = {} },
469 .not => return self.genNot(inst.castTag(.not).?),
470 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
471 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
472 }559 }
473 }560 }
474561
475 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {562 fn allocMem(self: *Self, inst: *ir.Inst, abi_size: u32, abi_align: u32) !u32 {
476 const elem_ty = inst.base.ty.elemType();
477 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
478 return self.fail(inst.base.src, "type '{}' too big to fit into stack frame", .{elem_ty});
479 };
480 // TODO swap this for inst.base.ty.ptrAlign
481 const abi_align = elem_ty.abiAlignment(self.target.*);
482 if (abi_align > self.stack_align)563 if (abi_align > self.stack_align)
483 self.stack_align = abi_align;564 self.stack_align = abi_align;
484 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];565 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
...@@ -488,10 +569,62 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -488,10 +569,62 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
488 if (branch.next_stack_offset > branch.max_end_stack)569 if (branch.next_stack_offset > branch.max_end_stack)
489 branch.max_end_stack = branch.next_stack_offset;570 branch.max_end_stack = branch.next_stack_offset;
490 try branch.stack.putNoClobber(self.gpa, offset, .{571 try branch.stack.putNoClobber(self.gpa, offset, .{
491 .inst = &inst.base,572 .inst = inst,
492 .size = abi_size,573 .size = abi_size,
493 });574 });
494 return MCValue{ .stack_offset = offset };575 return offset;
576 }
577
578 /// Use a pointer instruction as the basis for allocating stack memory.
579 fn allocMemPtr(self: *Self, inst: *ir.Inst) !u32 {
580 const elem_ty = inst.ty.elemType();
581 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
582 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
583 };
584 // TODO swap this for inst.ty.ptrAlign
585 const abi_align = elem_ty.abiAlignment(self.target.*);
586 return self.allocMem(inst, abi_size, abi_align);
587 }
588
589 fn allocRegOrMem(self: *Self, inst: *ir.Inst) !MCValue {
590 const elem_ty = inst.ty;
591 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
592 return self.fail(inst.src, "type '{}' too big to fit into stack frame", .{elem_ty});
593 };
594 const abi_align = elem_ty.abiAlignment(self.target.*);
595 if (abi_align > self.stack_align)
596 self.stack_align = abi_align;
597 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
598
599 // Make sure the type can fit in a register before we try to allocate one.
600 const ptr_bits = arch.ptrBitWidth();
601 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
602 if (abi_size <= ptr_bytes) {
603 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
604 if (branch.allocReg(inst)) |reg| {
605 return MCValue{ .register = registerAlias(reg, abi_size) };
606 }
607 }
608 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
609 return MCValue{ .stack_offset = stack_offset };
610 }
611
612 /// Does not "move" the instruction.
613 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
614 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
615 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
616
617 const reg = branch.allocReg(inst) orelse
618 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
619 const old_mcv = branch.inst_table.get(inst).?;
620 const new_mcv: MCValue = .{ .register = reg };
621 try self.genSetReg(inst.src, reg, old_mcv);
622 return new_mcv;
623 }
624
625 fn genAlloc(self: *Self, inst: *ir.Inst.NoOp) !MCValue {
626 const stack_offset = try self.allocMemPtr(&inst.base);
627 return MCValue{ .ptr_stack_offset = stack_offset };
495 }628 }
496629
497 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {630 fn genFloatCast(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
...@@ -572,6 +705,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -572,6 +705,87 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
572 }705 }
573 }706 }
574707
708 fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
709 const elem_ty = inst.base.ty;
710 if (!elem_ty.hasCodeGenBits())
711 return MCValue.none;
712 const ptr = try self.resolveInst(inst.operand);
713 const is_volatile = inst.operand.ty.isVolatilePtr();
714 if (inst.base.isUnused() and !is_volatile)
715 return MCValue.dead;
716 const dst_mcv: MCValue = blk: {
717 if (inst.base.operandDies(0) and ptr.isMutable()) {
718 // The MCValue that holds the pointer can be re-used as the value.
719 // TODO track this in the register/stack allocation metadata.
720 break :blk ptr;
721 } else {
722 break :blk try self.allocRegOrMem(&inst.base);
723 }
724 };
725 switch (ptr) {
726 .none => unreachable,
727 .undef => unreachable,
728 .unreach => unreachable,
729 .dead => unreachable,
730 .compare_flags_unsigned => unreachable,
731 .compare_flags_signed => unreachable,
732 .immediate => |imm| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .memory = imm }),
733 .ptr_stack_offset => |off| try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .stack_offset = off }),
734 .ptr_embedded_in_code => |off| {
735 try self.setRegOrMem(inst.base.src, elem_ty, dst_mcv, .{ .embedded_in_code = off });
736 },
737 .embedded_in_code => {
738 return self.fail(inst.base.src, "TODO implement loading from MCValue.embedded_in_code", .{});
739 },
740 .register => {
741 return self.fail(inst.base.src, "TODO implement loading from MCValue.register", .{});
742 },
743 .memory => {
744 return self.fail(inst.base.src, "TODO implement loading from MCValue.memory", .{});
745 },
746 .stack_offset => {
747 return self.fail(inst.base.src, "TODO implement loading from MCValue.stack_offset", .{});
748 },
749 }
750 return dst_mcv;
751 }
752
753 fn genStore(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
754 const ptr = try self.resolveInst(inst.lhs);
755 const value = try self.resolveInst(inst.rhs);
756 const elem_ty = inst.rhs.ty;
757 switch (ptr) {
758 .none => unreachable,
759 .undef => unreachable,
760 .unreach => unreachable,
761 .dead => unreachable,
762 .compare_flags_unsigned => unreachable,
763 .compare_flags_signed => unreachable,
764 .immediate => |imm| {
765 try self.setRegOrMem(inst.base.src, elem_ty, .{ .memory = imm }, value);
766 },
767 .ptr_stack_offset => |off| {
768 try self.genSetStack(inst.base.src, elem_ty, off, value);
769 },
770 .ptr_embedded_in_code => |off| {
771 try self.setRegOrMem(inst.base.src, elem_ty, .{ .embedded_in_code = off }, value);
772 },
773 .embedded_in_code => {
774 return self.fail(inst.base.src, "TODO implement storing to MCValue.embedded_in_code", .{});
775 },
776 .register => {
777 return self.fail(inst.base.src, "TODO implement storing to MCValue.register", .{});
778 },
779 .memory => {
780 return self.fail(inst.base.src, "TODO implement storing to MCValue.memory", .{});
781 },
782 .stack_offset => {
783 return self.fail(inst.base.src, "TODO implement storing to MCValue.stack_offset", .{});
784 },
785 }
786 return .none;
787 }
788
575 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {789 fn genSub(self: *Self, inst: *ir.Inst.BinOp) !MCValue {
576 // No side effects, so if it's unreferenced, do nothing.790 // No side effects, so if it's unreferenced, do nothing.
577 if (inst.base.isUnused())791 if (inst.base.isUnused())
...@@ -654,13 +868,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -654,13 +868,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
654 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {868 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
655 switch (dst_mcv) {869 switch (dst_mcv) {
656 .none => unreachable,870 .none => unreachable,
871 .undef => unreachable,
657 .dead, .unreach, .immediate => unreachable,872 .dead, .unreach, .immediate => unreachable,
658 .compare_flags_unsigned => unreachable,873 .compare_flags_unsigned => unreachable,
659 .compare_flags_signed => unreachable,874 .compare_flags_signed => unreachable,
875 .ptr_stack_offset => unreachable,
876 .ptr_embedded_in_code => unreachable,
660 .register => |dst_reg| {877 .register => |dst_reg| {
661 switch (src_mcv) {878 switch (src_mcv) {
662 .none => unreachable,879 .none => unreachable,
880 .undef => try self.genSetReg(src, dst_reg, .undef),
663 .dead, .unreach => unreachable,881 .dead, .unreach => unreachable,
882 .ptr_stack_offset => unreachable,
883 .ptr_embedded_in_code => unreachable,
664 .register => |src_reg| {884 .register => |src_reg| {
665 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });885 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
666 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });886 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
...@@ -743,6 +963,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -743,6 +963,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
743 for (info.args) |mc_arg, arg_i| {963 for (info.args) |mc_arg, arg_i| {
744 const arg = inst.args[arg_i];964 const arg = inst.args[arg_i];
745 const arg_mcv = try self.resolveInst(inst.args[arg_i]);965 const arg_mcv = try self.resolveInst(inst.args[arg_i]);
966 // Here we do not use setRegOrMem even though the logic is similar, because
967 // the function call will move the stack pointer, so the offsets are different.
746 switch (mc_arg) {968 switch (mc_arg) {
747 .none => continue,969 .none => continue,
748 .register => |reg| {970 .register => |reg| {
...@@ -754,6 +976,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -754,6 +976,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
754 // mov qword ptr [rsp + stack_offset], x976 // mov qword ptr [rsp + stack_offset], x
755 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});977 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
756 },978 },
979 .ptr_stack_offset => {
980 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
981 },
982 .ptr_embedded_in_code => {
983 return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
984 },
985 .undef => unreachable,
757 .immediate => unreachable,986 .immediate => unreachable,
758 .unreach => unreachable,987 .unreach => unreachable,
759 .dead => unreachable,988 .dead => unreachable,
...@@ -788,17 +1017,47 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -788,17 +1017,47 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
788 return info.return_value;1017 return info.return_value;
789 }1018 }
7901019
1020 fn genRef(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1021 const operand = try self.resolveInst(inst.operand);
1022 switch (operand) {
1023 .unreach => unreachable,
1024 .dead => unreachable,
1025 .none => return .none,
1026
1027 .immediate,
1028 .register,
1029 .ptr_stack_offset,
1030 .ptr_embedded_in_code,
1031 .compare_flags_unsigned,
1032 .compare_flags_signed,
1033 => {
1034 const stack_offset = try self.allocMemPtr(&inst.base);
1035 try self.genSetStack(inst.base.src, inst.operand.ty, stack_offset, operand);
1036 return MCValue{ .ptr_stack_offset = stack_offset };
1037 },
1038
1039 .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
1040 .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
1041 .memory => |vaddr| return MCValue{ .immediate = vaddr },
1042
1043 .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
1044 }
1045 }
1046
791 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {1047 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
792 try self.setRegOrStack(src, self.ret_mcv, mcv);1048 const ret_ty = self.fn_type.fnReturnType();
1049 try self.setRegOrMem(src, ret_ty, self.ret_mcv, mcv);
793 switch (arch) {1050 switch (arch) {
794 .i386 => {1051 .i386 => {
795 try self.code.append(0xc3); // ret1052 try self.code.append(0xc3); // ret
796 },1053 },
797 .x86_64 => {1054 .x86_64 => {
798 try self.code.appendSlice(&[_]u8{1055 // TODO when implementing defer, this will need to jump to the appropriate defer expression.
799 0x5d, // pop rbp1056 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
800 0xc3, // ret1057 // which is available if the jump is 127 bytes or less forward.
801 });1058 try self.code.resize(self.code.items.len + 5);
1059 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
1060 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
802 },1061 },
803 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),1062 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
804 }1063 }
...@@ -882,7 +1141,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -882,7 +1141,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
882 // test reg, 11141 // test reg, 1
883 // TODO detect al, ax, eax1142 // TODO detect al, ax, eax
884 try self.code.ensureCapacity(self.code.items.len + 4);1143 try self.code.ensureCapacity(self.code.items.len + 4);
885 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });1144 // TODO audit this codegen: we force w = true here to make
1145 // the value affect the big register
1146 self.rex(.{ .b = reg.isExtended(), .w = true });
886 self.code.appendSliceAssumeCapacity(&[_]u8{1147 self.code.appendSliceAssumeCapacity(&[_]u8{
887 0xf6,1148 0xf6,
888 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),1149 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
...@@ -938,6 +1199,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -938,6 +1199,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
938 switch (reloc) {1199 switch (reloc) {
939 .rel32 => |pos| {1200 .rel32 => |pos| {
940 const amt = self.code.items.len - (pos + 4);1201 const amt = self.code.items.len - (pos + 4);
1202 // If it wouldn't jump at all, elide it.
1203 if (amt == 0) {
1204 self.code.items.len -= 5;
1205 return;
1206 }
941 const s32_amt = math.cast(i32, amt) catch1207 const s32_amt = math.cast(i32, amt) catch
942 return self.fail(src, "unable to perform relocation: jump too far", .{});1208 return self.fail(src, "unable to perform relocation: jump too far", .{});
943 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);1209 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
...@@ -1042,25 +1308,141 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1042,25 +1308,141 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1042 }1308 }
10431309
1044 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.1310 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1045 fn setRegOrStack(self: *Self, src: usize, loc: MCValue, val: MCValue) !void {1311 fn setRegOrMem(self: *Self, src: usize, ty: Type, loc: MCValue, val: MCValue) !void {
1046 switch (loc) {1312 switch (loc) {
1047 .none => return,1313 .none => return,
1048 .register => |reg| return self.genSetReg(src, reg, val),1314 .register => |reg| return self.genSetReg(src, reg, val),
1049 .stack_offset => {1315 .stack_offset => |off| return self.genSetStack(src, ty, off, val),
1050 return self.fail(src, "TODO implement setRegOrStack for stack offset", .{});1316 .memory => {
1317 return self.fail(src, "TODO implement setRegOrMem for memory", .{});
1051 },1318 },
1052 else => unreachable,1319 else => unreachable,
1053 }1320 }
1054 }1321 }
10551322
1056 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {1323 fn genSetStack(self: *Self, src: usize, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1057 switch (arch) {1324 switch (arch) {
1058 .x86_64 => switch (mcv) {1325 .x86_64 => switch (mcv) {
1059 .dead => unreachable,1326 .dead => unreachable,
1327 .ptr_stack_offset => unreachable,
1328 .ptr_embedded_in_code => unreachable,
1060 .unreach, .none => return, // Nothing to do.1329 .unreach, .none => return, // Nothing to do.
1330 .undef => {
1331 if (!self.wantSafety())
1332 return; // The already existing value will do just fine.
1333 // TODO Upgrade this to a memset call when we have that available.
1334 switch (ty.abiSize(self.target.*)) {
1335 1 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaa }),
1336 2 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaa }),
1337 4 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
1338 8 => return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1339 else => return self.fail(src, "TODO implement memset", .{}),
1340 }
1341 },
1342 .compare_flags_unsigned => |op| {
1343 return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
1344 },
1345 .compare_flags_signed => |op| {
1346 return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
1347 },
1348 .immediate => |x_big| {
1349 const abi_size = ty.abiSize(self.target.*);
1350 const adj_off = stack_offset + abi_size;
1351 if (adj_off > 128) {
1352 return self.fail(src, "TODO implement set stack variable with large stack offset", .{});
1353 }
1354 try self.code.ensureCapacity(self.code.items.len + 8);
1355 switch (abi_size) {
1356 1 => {
1357 return self.fail(src, "TODO implement set abi_size=1 stack variable with immediate", .{});
1358 },
1359 2 => {
1360 return self.fail(src, "TODO implement set abi_size=2 stack variable with immediate", .{});
1361 },
1362 4 => {
1363 const x = @intCast(u32, x_big);
1364 // We have a positive stack offset value but we want a twos complement negative
1365 // offset from rbp, which is at the top of the stack frame.
1366 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1367 const twos_comp = @bitCast(u8, negative_offset);
1368 // mov DWORD PTR [rbp+offset], immediate
1369 self.code.appendSliceAssumeCapacity(&[_]u8{ 0xc7, 0x45, twos_comp });
1370 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
1371 },
1372 8 => {
1373 return self.fail(src, "TODO implement set abi_size=8 stack variable with immediate", .{});
1374 },
1375 else => {
1376 return self.fail(src, "TODO implement set abi_size=large stack variable with immediate", .{});
1377 },
1378 }
1379 if (x_big <= math.maxInt(u32)) {} else {
1380 return self.fail(src, "TODO implement set stack variable with large immediate", .{});
1381 }
1382 },
1383 .embedded_in_code => |code_offset| {
1384 return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{});
1385 },
1386 .register => |reg| {
1387 const abi_size = ty.abiSize(self.target.*);
1388 const adj_off = stack_offset + abi_size;
1389 try self.code.ensureCapacity(self.code.items.len + 7);
1390 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1391 const reg_id: u8 = @truncate(u3, reg.id());
1392 if (adj_off <= 128) {
1393 // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx
1394 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1395 const negative_offset = @intCast(i8, -@intCast(i32, adj_off));
1396 const twos_comp = @bitCast(u8, negative_offset);
1397 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM, twos_comp });
1398 } else if (adj_off <= 2147483648) {
1399 // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx
1400 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1401 const negative_offset = @intCast(i32, -@intCast(i33, adj_off));
1402 const twos_comp = @bitCast(u32, negative_offset);
1403 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x89, RM });
1404 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1405 } else {
1406 return self.fail(src, "stack offset too large", .{});
1407 }
1408 },
1409 .memory => |vaddr| {
1410 return self.fail(src, "TODO implement set stack variable from memory vaddr", .{});
1411 },
1412 .stack_offset => |off| {
1413 if (stack_offset == off)
1414 return; // Copy stack variable to itself; nothing to do.
1415 return self.fail(src, "TODO implement copy stack variable to stack variable", .{});
1416 },
1417 },
1418 else => return self.fail(src, "TODO implement getSetStack for {}", .{self.target.cpu.arch}),
1419 }
1420 }
1421
1422 fn genSetReg(self: *Self, src: usize, reg: Register, mcv: MCValue) InnerError!void {
1423 switch (arch) {
1424 .x86_64 => switch (mcv) {
1425 .dead => unreachable,
1426 .ptr_stack_offset => unreachable,
1427 .ptr_embedded_in_code => unreachable,
1428 .unreach, .none => return, // Nothing to do.
1429 .undef => {
1430 if (!self.wantSafety())
1431 return; // The already existing value will do just fine.
1432 // Write the debug undefined value.
1433 switch (reg.size()) {
1434 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
1435 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
1436 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
1437 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
1438 else => unreachable,
1439 }
1440 },
1061 .compare_flags_unsigned => |op| {1441 .compare_flags_unsigned => |op| {
1062 try self.code.ensureCapacity(self.code.items.len + 3);1442 try self.code.ensureCapacity(self.code.items.len + 3);
1063 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });1443 // TODO audit this codegen: we force w = true here to make
1444 // the value affect the big register
1445 self.rex(.{ .b = reg.isExtended(), .w = true });
1064 const opcode: u8 = switch (op) {1446 const opcode: u8 = switch (op) {
1065 .gte => 0x93,1447 .gte => 0x93,
1066 .gt => 0x97,1448 .gt => 0x97,
...@@ -1076,9 +1458,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1076,9 +1458,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1076 return self.fail(src, "TODO set register with compare flags value (signed)", .{});1458 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
1077 },1459 },
1078 .immediate => |x| {1460 .immediate => |x| {
1079 if (reg.size() != 64) {
1080 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1081 }
1082 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit1461 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
1083 // register is the fastest way to zero a register.1462 // register is the fastest way to zero a register.
1084 if (x == 0) {1463 if (x == 0) {
...@@ -1131,16 +1510,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1131,16 +1510,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1131 //1510 //
1132 // In this case, the encoding of the REX byte is 0b0100100B1511 // In this case, the encoding of the REX byte is 0b0100100B
1133 try self.code.ensureCapacity(self.code.items.len + 10);1512 try self.code.ensureCapacity(self.code.items.len + 10);
1134 self.rex(.{ .w = true, .b = reg.isExtended() });1513 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1135 self.code.items.len += 9;1514 self.code.items.len += 9;
1136 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);1515 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1137 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];1516 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1138 mem.writeIntLittle(u64, imm_ptr, x);1517 mem.writeIntLittle(u64, imm_ptr, x);
1139 },1518 },
1140 .embedded_in_code => |code_offset| {1519 .embedded_in_code => |code_offset| {
1141 if (reg.size() != 64) {
1142 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1143 }
1144 // We need the offset from RIP in a signed i32 twos complement.1520 // We need the offset from RIP in a signed i32 twos complement.
1145 // The instruction is 7 bytes long and RIP points to the next instruction.1521 // The instruction is 7 bytes long and RIP points to the next instruction.
1146 try self.code.ensureCapacity(self.code.items.len + 7);1522 try self.code.ensureCapacity(self.code.items.len + 7);
...@@ -1148,7 +1524,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1148,7 +1524,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1148 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three1524 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1149 // bits as five.1525 // bits as five.
1150 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.1526 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1151 self.rex(.{ .w = true, .b = reg.isExtended() });1527 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1152 self.code.items.len += 6;1528 self.code.items.len += 6;
1153 const rip = self.code.items.len;1529 const rip = self.code.items.len;
1154 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);1530 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
...@@ -1160,12 +1536,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1160,12 +1536,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1160 },1536 },
1161 .register => |src_reg| {1537 .register => |src_reg| {
1162 // If the registers are the same, nothing to do.1538 // If the registers are the same, nothing to do.
1163 if (src_reg == reg)1539 if (src_reg.id() == reg.id())
1164 return;1540 return;
11651541
1166 if (reg.size() != 64) {
1167 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1168 }
1169 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.1542 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1170 // This is thus three bytes: REX 0x8B R/M.1543 // This is thus three bytes: REX 0x8B R/M.
1171 // If the destination is extended, the R field must be 1.1544 // If the destination is extended, the R field must be 1.
...@@ -1173,14 +1546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1173,14 +1546,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1173 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle1546 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1174 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.1547 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1175 try self.code.ensureCapacity(self.code.items.len + 3);1548 try self.code.ensureCapacity(self.code.items.len + 3);
1176 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });1549 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended(), .b = src_reg.isExtended() });
1177 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);1550 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1178 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });1551 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1179 },1552 },
1180 .memory => |x| {1553 .memory => |x| {
1181 if (reg.size() != 64) {
1182 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1183 }
1184 if (x <= math.maxInt(u32)) {1554 if (x <= math.maxInt(u32)) {
1185 // Moving from memory to a register is a variant of `8B /r`.1555 // Moving from memory to a register is a variant of `8B /r`.
1186 // Since we're using 64-bit moves, we require a REX.1556 // Since we're using 64-bit moves, we require a REX.
...@@ -1190,7 +1560,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1190,7 +1560,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1190 // 0b00RRR100, where RRR is the lower three bits of the register ID.1560 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1191 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.1561 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1192 try self.code.ensureCapacity(self.code.items.len + 8);1562 try self.code.ensureCapacity(self.code.items.len + 8);
1193 self.rex(.{ .w = true, .b = reg.isExtended() });1563 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() });
1194 self.code.appendSliceAssumeCapacity(&[_]u8{1564 self.code.appendSliceAssumeCapacity(&[_]u8{
1195 0x8B,1565 0x8B,
1196 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R1566 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
...@@ -1218,7 +1588,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1218,7 +1588,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1218 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with1588 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1219 // this instruction.1589 // this instruction.
1220 const id3 = @truncate(u3, reg.id());1590 const id3 = @truncate(u3, reg.id());
1221 std.debug.assert(id3 != 4 and id3 != 5);1591 assert(id3 != 4 and id3 != 5);
12221592
1223 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.1593 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1224 try self.genSetReg(src, reg, MCValue{ .immediate = x });1594 try self.genSetReg(src, reg, MCValue{ .immediate = x });
...@@ -1233,14 +1603,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1233,14 +1603,34 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1233 //1603 //
1234 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*1604 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1235 // register operands need to be marked as extended.1605 // register operands need to be marked as extended.
1236 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });1606 self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() });
1237 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());1607 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1238 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });1608 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1239 }1609 }
1240 }1610 }
1241 },1611 },
1242 .stack_offset => |off| {1612 .stack_offset => |unadjusted_off| {
1243 return self.fail(src, "TODO implement genSetReg for stack variables", .{});1613 try self.code.ensureCapacity(self.code.items.len + 7);
1614 const size_bytes = @divExact(reg.size(), 8);
1615 const off = unadjusted_off + size_bytes;
1616 self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() });
1617 const reg_id: u8 = @truncate(u3, reg.id());
1618 if (off <= 128) {
1619 // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f]
1620 const RM = @as(u8, 0b01_000_101) | (reg_id << 3);
1621 const negative_offset = @intCast(i8, -@intCast(i32, off));
1622 const twos_comp = @bitCast(u8, negative_offset);
1623 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM, twos_comp });
1624 } else if (off <= 2147483648) {
1625 // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80]
1626 const RM = @as(u8, 0b10_000_101) | (reg_id << 3);
1627 const negative_offset = @intCast(i32, -@intCast(i33, off));
1628 const twos_comp = @bitCast(u32, negative_offset);
1629 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM });
1630 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp);
1631 } else {
1632 return self.fail(src, "stack offset too large", .{});
1633 }
1244 },1634 },
1245 },1635 },
1246 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),1636 else => return self.fail(src, "TODO implement getSetReg for {}", .{self.target.cpu.arch}),
...@@ -1279,24 +1669,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1279,24 +1669,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1279 }1669 }
1280 }1670 }
12811671
1282 /// Does not "move" the instruction.
1283 fn copyToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1284 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1285 try branch.registers.ensureCapacity(self.gpa, branch.registers.items().len + 1);
1286 try branch.inst_table.ensureCapacity(self.gpa, branch.inst_table.items().len + 1);
1287
1288 const free_index = @ctz(FreeRegInt, branch.free_registers);
1289 if (free_index >= callee_preserved_regs.len)
1290 return self.fail(inst.src, "TODO implement spilling register to stack", .{});
1291 branch.free_registers &= ~(@as(FreeRegInt, 1) << free_index);
1292 const reg = callee_preserved_regs[free_index];
1293 branch.registers.putAssumeCapacityNoClobber(reg, .{ .inst = inst });
1294 const old_mcv = branch.inst_table.get(inst).?;
1295 const new_mcv: MCValue = .{ .register = reg };
1296 try self.genSetReg(inst.src, reg, old_mcv);
1297 return new_mcv;
1298 }
1299
1300 /// If the MCValue is an immediate, and it does not fit within this type,1672 /// If the MCValue is an immediate, and it does not fit within this type,
1301 /// we put it in a register.1673 /// we put it in a register.
1302 /// A potential opportunity for future optimization here would be keeping track1674 /// A potential opportunity for future optimization here would be keeping track
...@@ -1324,6 +1696,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1324,6 +1696,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1324 }1696 }
13251697
1326 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {1698 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1699 if (typed_value.val.isUndef())
1700 return MCValue.undef;
1327 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1701 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1328 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1702 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1329 switch (typed_value.ty.zigTypeTag()) {1703 switch (typed_value.ty.zigTypeTag()) {
...@@ -1398,11 +1772,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1398,11 +1772,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1398 for (param_types) |ty, i| {1772 for (param_types) |ty, i| {
1399 switch (ty.zigTypeTag()) {1773 switch (ty.zigTypeTag()) {
1400 .Bool, .Int => {1774 .Bool, .Int => {
1775 const param_size = @intCast(u32, ty.abiSize(self.target.*));
1401 if (next_int_reg >= c_abi_int_param_regs.len) {1776 if (next_int_reg >= c_abi_int_param_regs.len) {
1402 result.args[i] = .{ .stack_offset = next_stack_offset };1777 result.args[i] = .{ .stack_offset = next_stack_offset };
1403 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));1778 next_stack_offset += param_size;
1404 } else {1779 } else {
1405 result.args[i] = .{ .register = c_abi_int_param_regs[next_int_reg] };1780 const aliased_reg = registerAlias(
1781 c_abi_int_param_regs[next_int_reg],
1782 param_size,
1783 );
1784 result.args[i] = .{ .register = aliased_reg };
1406 next_int_reg += 1;1785 next_int_reg += 1;
1407 }1786 }
1408 },1787 },
...@@ -1426,7 +1805,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1426,7 +1805,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1426 .x86_64 => switch (cc) {1805 .x86_64 => switch (cc) {
1427 .Naked => unreachable,1806 .Naked => unreachable,
1428 .Unspecified, .C => {1807 .Unspecified, .C => {
1429 result.return_value = .{ .register = c_abi_int_return_regs[0] };1808 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
1809 const aliased_reg = registerAlias(c_abi_int_return_regs[0], ret_ty_size);
1810 result.return_value = .{ .register = aliased_reg };
1430 },1811 },
1431 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),1812 else => return self.fail(src, "TODO implement function return values for {}", .{cc}),
1432 },1813 },
...@@ -1435,6 +1816,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1435,6 +1816,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1435 return result;1816 return result;
1436 }1817 }
14371818
1819 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
1820 fn wantSafety(self: *Self) bool {
1821 return switch (self.bin_file.base.options.optimize_mode) {
1822 .Debug => true,
1823 .ReleaseSafe => true,
1824 .ReleaseFast => false,
1825 .ReleaseSmall => false,
1826 };
1827 }
1828
1438 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {1829 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1439 @setCold(true);1830 @setCold(true);
1440 assert(self.err_msg == null);1831 assert(self.err_msg == null);
...@@ -1463,5 +1854,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1463,5 +1854,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1463 fn parseRegName(name: []const u8) ?Register {1854 fn parseRegName(name: []const u8) ?Register {
1464 return std.meta.stringToEnum(Register, name);1855 return std.meta.stringToEnum(Register, name);
1465 }1856 }
1857
1858 fn registerAlias(reg: Register, size_bytes: u32) Register {
1859 switch (arch) {
1860 // For x86_64 we have to pick a smaller register alias depending on abi size.
1861 .x86_64 => switch (size_bytes) {
1862 1 => return reg.to8(),
1863 2 => return reg.to16(),
1864 4 => return reg.to32(),
1865 8 => return reg.to64(),
1866 else => unreachable,
1867 },
1868 else => return reg,
1869 }
1870 }
1466 };1871 };
1467}1872}
src-self-hosted/codegen/x86_64.zig+20
...@@ -81,6 +81,26 @@ pub const Register = enum(u8) {...@@ -81,6 +81,26 @@ pub const Register = enum(u8) {
81 else => null,81 else => null,
82 };82 };
83 }83 }
84
85 /// Convert from any register to its 64 bit alias.
86 pub fn to64(self: Register) Register {
87 return @intToEnum(Register, self.id());
88 }
89
90 /// Convert from any register to its 32 bit alias.
91 pub fn to32(self: Register) Register {
92 return @intToEnum(Register, @as(u8, self.id()) + 16);
93 }
94
95 /// Convert from any register to its 16 bit alias.
96 pub fn to16(self: Register) Register {
97 return @intToEnum(Register, @as(u8, self.id()) + 32);
98 }
99
100 /// Convert from any register to its 8 bit alias.
101 pub fn to8(self: Register) Register {
102 return @intToEnum(Register, @as(u8, self.id()) + 48);
103 }
84};104};
85105
86// zig fmt: on106// zig fmt: on
src-self-hosted/ir.zig+9-2
...@@ -67,9 +67,14 @@ pub const Inst = struct {...@@ -67,9 +67,14 @@ pub const Inst = struct {
67 constant,67 constant,
68 isnonnull,68 isnonnull,
69 isnull,69 isnull,
70 /// Read a value from a pointer.
71 load,
70 ptrtoint,72 ptrtoint,
73 ref,
71 ret,74 ret,
72 retvoid,75 retvoid,
76 /// Write a value to a pointer. LHS is pointer, RHS is value.
77 store,
73 sub,78 sub,
74 unreach,79 unreach,
75 not,80 not,
...@@ -85,6 +90,7 @@ pub const Inst = struct {...@@ -85,6 +90,7 @@ pub const Inst = struct {
85 .breakpoint,90 .breakpoint,
86 => NoOp,91 => NoOp,
8792
93 .ref,
88 .ret,94 .ret,
89 .bitcast,95 .bitcast,
90 .not,96 .not,
...@@ -93,6 +99,7 @@ pub const Inst = struct {...@@ -93,6 +99,7 @@ pub const Inst = struct {
93 .ptrtoint,99 .ptrtoint,
94 .floatcast,100 .floatcast,
95 .intcast,101 .intcast,
102 .load,
96 => UnOp,103 => UnOp,
97104
98 .add,105 .add,
...@@ -103,6 +110,7 @@ pub const Inst = struct {...@@ -103,6 +110,7 @@ pub const Inst = struct {
103 .cmp_gte,110 .cmp_gte,
104 .cmp_gt,111 .cmp_gt,
105 .cmp_neq,112 .cmp_neq,
113 .store,
106 => BinOp,114 => BinOp,
107115
108 .assembly => Assembly,116 .assembly => Assembly,
...@@ -157,8 +165,7 @@ pub const Inst = struct {...@@ -157,8 +165,7 @@ pub const Inst = struct {
157165
158 /// Returns `null` if runtime-known.166 /// Returns `null` if runtime-known.
159 pub fn value(base: *Inst) ?Value {167 pub fn value(base: *Inst) ?Value {
160 if (base.ty.onePossibleValue())168 if (base.ty.onePossibleValue()) |opv| return opv;
161 return Value.initTag(.the_one_possible_value);
162169
163 const inst = base.cast(Constant) orelse return null;170 const inst = base.cast(Constant) orelse return null;
164 return inst.val;171 return inst.val;
src-self-hosted/link.zig+63-57
...@@ -16,6 +16,7 @@ pub const Options = struct {...@@ -16,6 +16,7 @@ pub const Options = struct {
16 output_mode: std.builtin.OutputMode,16 output_mode: std.builtin.OutputMode,
17 link_mode: std.builtin.LinkMode,17 link_mode: std.builtin.LinkMode,
18 object_format: std.builtin.ObjectFormat,18 object_format: std.builtin.ObjectFormat,
19 optimize_mode: std.builtin.Mode,
19 /// Used for calculating how much space to reserve for symbols in case the binary file20 /// Used for calculating how much space to reserve for symbols in case the binary file
20 /// does not already have a symbol table.21 /// does not already have a symbol table.
21 symbol_count_hint: u64 = 32,22 symbol_count_hint: u64 = 32,
...@@ -66,6 +67,7 @@ pub fn writeFilePath(...@@ -66,6 +67,7 @@ pub fn writeFilePath(
66 .link_mode = module.link_mode,67 .link_mode = module.link_mode,
67 .object_format = module.object_format,68 .object_format = module.object_format,
68 .symbol_count_hint = module.decls.items.len,69 .symbol_count_hint = module.decls.items.len,
70 .optimize_mode = module.optimize_mode,
69 };71 };
70 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });72 const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
71 defer af.deinit();73 defer af.deinit();
...@@ -88,9 +90,12 @@ pub fn writeFilePath(...@@ -88,9 +90,12 @@ pub fn writeFilePath(
8890
89fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {91fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
90 return File.C{92 return File.C{
93 .base = .{
94 .tag = .c,
95 .options = options,
96 },
91 .allocator = allocator,97 .allocator = allocator,
92 .file = file,98 .file = file,
93 .options = options,
94 .main = std.ArrayList(u8).init(allocator),99 .main = std.ArrayList(u8).init(allocator),
95 .header = std.ArrayList(u8).init(allocator),100 .header = std.ArrayList(u8).init(allocator),
96 .constants = std.ArrayList(u8).init(allocator),101 .constants = std.ArrayList(u8).init(allocator),
...@@ -114,6 +119,8 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File...@@ -114,6 +119,8 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File
114119
115pub const File = struct {120pub const File = struct {
116 tag: Tag,121 tag: Tag,
122 options: Options,
123
117 pub fn cast(base: *File, comptime T: type) ?*T {124 pub fn cast(base: *File, comptime T: type) ?*T {
118 if (base.tag != T.base_tag)125 if (base.tag != T.base_tag)
119 return null;126 return null;
...@@ -123,47 +130,47 @@ pub const File = struct {...@@ -123,47 +130,47 @@ pub const File = struct {
123130
124 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {131 pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
125 switch (base.tag) {132 switch (base.tag) {
126 .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),133 .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
127 .C => {},134 .c => {},
128 }135 }
129 }136 }
130137
131 pub fn makeExecutable(base: *File) !void {138 pub fn makeExecutable(base: *File) !void {
132 switch (base.tag) {139 switch (base.tag) {
133 .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),140 .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
134 .C => unreachable,141 .c => unreachable,
135 }142 }
136 }143 }
137144
138 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {145 pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
139 switch (base.tag) {146 switch (base.tag) {
140 .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),147 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
141 .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),148 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
142 }149 }
143 }150 }
144151
145 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {152 pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
146 switch (base.tag) {153 switch (base.tag) {
147 .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),154 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
148 .C => {},155 .c => {},
149 }156 }
150 }157 }
151158
152 pub fn deinit(base: *File) void {159 pub fn deinit(base: *File) void {
153 switch (base.tag) {160 switch (base.tag) {
154 .Elf => @fieldParentPtr(Elf, "base", base).deinit(),161 .elf => @fieldParentPtr(Elf, "base", base).deinit(),
155 .C => @fieldParentPtr(C, "base", base).deinit(),162 .c => @fieldParentPtr(C, "base", base).deinit(),
156 }163 }
157 }164 }
158165
159 pub fn destroy(base: *File) void {166 pub fn destroy(base: *File) void {
160 switch (base.tag) {167 switch (base.tag) {
161 .Elf => {168 .elf => {
162 const parent = @fieldParentPtr(Elf, "base", base);169 const parent = @fieldParentPtr(Elf, "base", base);
163 parent.deinit();170 parent.deinit();
164 parent.allocator.destroy(parent);171 parent.allocator.destroy(parent);
165 },172 },
166 .C => {173 .c => {
167 const parent = @fieldParentPtr(C, "base", base);174 const parent = @fieldParentPtr(C, "base", base);
168 parent.deinit();175 parent.deinit();
169 parent.allocator.destroy(parent);176 parent.allocator.destroy(parent);
...@@ -173,29 +180,22 @@ pub const File = struct {...@@ -173,29 +180,22 @@ pub const File = struct {
173180
174 pub fn flush(base: *File) !void {181 pub fn flush(base: *File) !void {
175 try switch (base.tag) {182 try switch (base.tag) {
176 .Elf => @fieldParentPtr(Elf, "base", base).flush(),183 .elf => @fieldParentPtr(Elf, "base", base).flush(),
177 .C => @fieldParentPtr(C, "base", base).flush(),184 .c => @fieldParentPtr(C, "base", base).flush(),
178 };185 };
179 }186 }
180187
181 pub fn freeDecl(base: *File, decl: *Module.Decl) void {188 pub fn freeDecl(base: *File, decl: *Module.Decl) void {
182 switch (base.tag) {189 switch (base.tag) {
183 .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),190 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
184 .C => unreachable,191 .c => unreachable,
185 }192 }
186 }193 }
187194
188 pub fn errorFlags(base: *File) ErrorFlags {195 pub fn errorFlags(base: *File) ErrorFlags {
189 return switch (base.tag) {196 return switch (base.tag) {
190 .Elf => @fieldParentPtr(Elf, "base", base).error_flags,197 .elf => @fieldParentPtr(Elf, "base", base).error_flags,
191 .C => return .{ .no_entry_point_found = false },198 .c => return .{ .no_entry_point_found = false },
192 };
193 }
194
195 pub fn options(base: *File) Options {
196 return switch (base.tag) {
197 .Elf => @fieldParentPtr(Elf, "base", base).options,
198 .C => @fieldParentPtr(C, "base", base).options,
199 };199 };
200 }200 }
201201
...@@ -207,14 +207,14 @@ pub const File = struct {...@@ -207,14 +207,14 @@ pub const File = struct {
207 exports: []const *Module.Export,207 exports: []const *Module.Export,
208 ) !void {208 ) !void {
209 switch (base.tag) {209 switch (base.tag) {
210 .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),210 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
211 .C => return {},211 .c => return {},
212 }212 }
213 }213 }
214214
215 pub const Tag = enum {215 pub const Tag = enum {
216 Elf,216 elf,
217 C,217 c,
218 };218 };
219219
220 pub const ErrorFlags = struct {220 pub const ErrorFlags = struct {
...@@ -222,15 +222,15 @@ pub const File = struct {...@@ -222,15 +222,15 @@ pub const File = struct {
222 };222 };
223223
224 pub const C = struct {224 pub const C = struct {
225 pub const base_tag: Tag = .C;225 pub const base_tag: Tag = .c;
226 base: File = File{ .tag = base_tag },226
227 base: File,
227228
228 allocator: *Allocator,229 allocator: *Allocator,
229 header: std.ArrayList(u8),230 header: std.ArrayList(u8),
230 constants: std.ArrayList(u8),231 constants: std.ArrayList(u8),
231 main: std.ArrayList(u8),232 main: std.ArrayList(u8),
232 file: ?fs.File,233 file: ?fs.File,
233 options: Options,
234 called: std.StringHashMap(void),234 called: std.StringHashMap(void),
235 need_stddef: bool = false,235 need_stddef: bool = false,
236 need_stdint: bool = false,236 need_stdint: bool = false,
...@@ -294,13 +294,13 @@ pub const File = struct {...@@ -294,13 +294,13 @@ pub const File = struct {
294 };294 };
295295
296 pub const Elf = struct {296 pub const Elf = struct {
297 pub const base_tag: Tag = .Elf;297 pub const base_tag: Tag = .elf;
298 base: File = File{ .tag = base_tag },298
299 base: File,
299300
300 allocator: *Allocator,301 allocator: *Allocator,
301 file: ?fs.File,302 file: ?fs.File,
302 owns_file_handle: bool,303 owns_file_handle: bool,
303 options: Options,
304 ptr_width: enum { p32, p64 },304 ptr_width: enum { p32, p64 },
305305
306 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.306 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
...@@ -460,13 +460,13 @@ pub const File = struct {...@@ -460,13 +460,13 @@ pub const File = struct {
460 self.file = try dir.createFile(sub_path, .{460 self.file = try dir.createFile(sub_path, .{
461 .truncate = false,461 .truncate = false,
462 .read = true,462 .read = true,
463 .mode = determineMode(self.options),463 .mode = determineMode(self.base.options),
464 });464 });
465 }465 }
466466
467 /// Returns end pos of collision, if any.467 /// Returns end pos of collision, if any.
468 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {468 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
469 const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;469 const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
470 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);470 const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
471 if (start < ehdr_size)471 if (start < ehdr_size)
472 return ehdr_size;472 return ehdr_size;
...@@ -569,7 +569,7 @@ pub const File = struct {...@@ -569,7 +569,7 @@ pub const File = struct {
569 };569 };
570 if (self.phdr_load_re_index == null) {570 if (self.phdr_load_re_index == null) {
571 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);571 self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
572 const file_size = self.options.program_code_size_hint;572 const file_size = self.base.options.program_code_size_hint;
573 const p_align = 0x1000;573 const p_align = 0x1000;
574 const off = self.findFreeSpace(file_size, p_align);574 const off = self.findFreeSpace(file_size, p_align);
575 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });575 std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
...@@ -588,7 +588,7 @@ pub const File = struct {...@@ -588,7 +588,7 @@ pub const File = struct {
588 }588 }
589 if (self.phdr_got_index == null) {589 if (self.phdr_got_index == null) {
590 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);590 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
591 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;591 const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
592 // We really only need ptr alignment but since we are using PROGBITS, linux requires592 // We really only need ptr alignment but since we are using PROGBITS, linux requires
593 // page align.593 // page align.
594 const p_align = 0x1000;594 const p_align = 0x1000;
...@@ -671,7 +671,7 @@ pub const File = struct {...@@ -671,7 +671,7 @@ pub const File = struct {
671 self.symtab_section_index = @intCast(u16, self.sections.items.len);671 self.symtab_section_index = @intCast(u16, self.sections.items.len);
672 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);672 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
673 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);673 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
674 const file_size = self.options.symbol_count_hint * each_size;674 const file_size = self.base.options.symbol_count_hint * each_size;
675 const off = self.findFreeSpace(file_size, min_align);675 const off = self.findFreeSpace(file_size, min_align);
676 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });676 std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
677677
...@@ -726,7 +726,7 @@ pub const File = struct {...@@ -726,7 +726,7 @@ pub const File = struct {
726726
727 /// Commit pending changes and write headers.727 /// Commit pending changes and write headers.
728 pub fn flush(self: *Elf) !void {728 pub fn flush(self: *Elf) !void {
729 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();729 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
730730
731 // Unfortunately these have to be buffered and done at the end because ELF does not allow731 // Unfortunately these have to be buffered and done at the end because ELF does not allow
732 // mixing local and global symbols within a symbol table.732 // mixing local and global symbols within a symbol table.
...@@ -845,7 +845,7 @@ pub const File = struct {...@@ -845,7 +845,7 @@ pub const File = struct {
845 }845 }
846 self.shdr_table_dirty = false;846 self.shdr_table_dirty = false;
847 }847 }
848 if (self.entry_addr == null and self.options.output_mode == .Exe) {848 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
849 std.log.debug(.link, "no_entry_point_found = true\n", .{});849 std.log.debug(.link, "no_entry_point_found = true\n", .{});
850 self.error_flags.no_entry_point_found = true;850 self.error_flags.no_entry_point_found = true;
851 } else {851 } else {
...@@ -875,7 +875,7 @@ pub const File = struct {...@@ -875,7 +875,7 @@ pub const File = struct {
875 };875 };
876 index += 1;876 index += 1;
877877
878 const endian = self.options.target.cpu.arch.endian();878 const endian = self.base.options.target.cpu.arch.endian();
879 hdr_buf[index] = switch (endian) {879 hdr_buf[index] = switch (endian) {
880 .Little => elf.ELFDATA2LSB,880 .Little => elf.ELFDATA2LSB,
881 .Big => elf.ELFDATA2MSB,881 .Big => elf.ELFDATA2MSB,
...@@ -893,10 +893,10 @@ pub const File = struct {...@@ -893,10 +893,10 @@ pub const File = struct {
893893
894 assert(index == 16);894 assert(index == 16);
895895
896 const elf_type = switch (self.options.output_mode) {896 const elf_type = switch (self.base.options.output_mode) {
897 .Exe => elf.ET.EXEC,897 .Exe => elf.ET.EXEC,
898 .Obj => elf.ET.REL,898 .Obj => elf.ET.REL,
899 .Lib => switch (self.options.link_mode) {899 .Lib => switch (self.base.options.link_mode) {
900 .Static => elf.ET.REL,900 .Static => elf.ET.REL,
901 .Dynamic => elf.ET.DYN,901 .Dynamic => elf.ET.DYN,
902 },902 },
...@@ -904,7 +904,7 @@ pub const File = struct {...@@ -904,7 +904,7 @@ pub const File = struct {
904 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);904 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
905 index += 2;905 index += 2;
906906
907 const machine = self.options.target.cpu.arch.toElfMachine();907 const machine = self.base.options.target.cpu.arch.toElfMachine();
908 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);908 mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
909 index += 2;909 index += 2;
910910
...@@ -1216,7 +1216,7 @@ pub const File = struct {...@@ -1216,7 +1216,7 @@ pub const File = struct {
1216 },1216 },
1217 };1217 };
12181218
1219 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1219 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
12201220
1221 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {1221 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
1222 .Fn => elf.STT_FUNC,1222 .Fn => elf.STT_FUNC,
...@@ -1361,9 +1361,9 @@ pub const File = struct {...@@ -1361,9 +1361,9 @@ pub const File = struct {
1361 }1361 }
13621362
1363 fn writeProgHeader(self: *Elf, index: usize) !void {1363 fn writeProgHeader(self: *Elf, index: usize) !void {
1364 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1364 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1365 const offset = self.program_headers.items[index].p_offset;1365 const offset = self.program_headers.items[index].p_offset;
1366 switch (self.options.target.cpu.arch.ptrBitWidth()) {1366 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
1367 32 => {1367 32 => {
1368 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};1368 var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
1369 if (foreign_endian) {1369 if (foreign_endian) {
...@@ -1383,9 +1383,9 @@ pub const File = struct {...@@ -1383,9 +1383,9 @@ pub const File = struct {
1383 }1383 }
13841384
1385 fn writeSectHeader(self: *Elf, index: usize) !void {1385 fn writeSectHeader(self: *Elf, index: usize) !void {
1386 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1386 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1387 const offset = self.sections.items[index].sh_offset;1387 const offset = self.sections.items[index].sh_offset;
1388 switch (self.options.target.cpu.arch.ptrBitWidth()) {1388 switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
1389 32 => {1389 32 => {
1390 var shdr: [1]elf.Elf32_Shdr = undefined;1390 var shdr: [1]elf.Elf32_Shdr = undefined;
1391 shdr[0] = sectHeaderTo32(self.sections.items[index]);1391 shdr[0] = sectHeaderTo32(self.sections.items[index]);
...@@ -1433,7 +1433,7 @@ pub const File = struct {...@@ -1433,7 +1433,7 @@ pub const File = struct {
14331433
1434 self.offset_table_count_dirty = false;1434 self.offset_table_count_dirty = false;
1435 }1435 }
1436 const endian = self.options.target.cpu.arch.endian();1436 const endian = self.base.options.target.cpu.arch.endian();
1437 const off = shdr.sh_offset + @as(u64, entry_size) * index;1437 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1438 switch (self.ptr_width) {1438 switch (self.ptr_width) {
1439 .p32 => {1439 .p32 => {
...@@ -1475,7 +1475,7 @@ pub const File = struct {...@@ -1475,7 +1475,7 @@ pub const File = struct {
1475 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later1475 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1476 self.shdr_table_dirty = true; // TODO look into only writing one section1476 self.shdr_table_dirty = true; // TODO look into only writing one section
1477 }1477 }
1478 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1478 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1479 switch (self.ptr_width) {1479 switch (self.ptr_width) {
1480 .p32 => {1480 .p32 => {
1481 var sym = [1]elf.Elf32_Sym{1481 var sym = [1]elf.Elf32_Sym{
...@@ -1511,7 +1511,7 @@ pub const File = struct {...@@ -1511,7 +1511,7 @@ pub const File = struct {
1511 .p32 => @sizeOf(elf.Elf32_Sym),1511 .p32 => @sizeOf(elf.Elf32_Sym),
1512 .p64 => @sizeOf(elf.Elf64_Sym),1512 .p64 => @sizeOf(elf.Elf64_Sym),
1513 };1513 };
1514 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1514 const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1515 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;1515 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1516 switch (self.ptr_width) {1516 switch (self.ptr_width) {
1517 .p32 => {1517 .p32 => {
...@@ -1577,9 +1577,12 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi...@@ -1577,9 +1577,12 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi
1577 }1577 }
15781578
1579 var self: File.Elf = .{1579 var self: File.Elf = .{
1580 .base = .{
1581 .tag = .elf,
1582 .options = options,
1583 },
1580 .allocator = allocator,1584 .allocator = allocator,
1581 .file = file,1585 .file = file,
1582 .options = options,
1583 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {1586 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1584 32 => .p32,1587 32 => .p32,
1585 64 => .p64,1588 64 => .p64,
...@@ -1637,10 +1640,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil...@@ -1637,10 +1640,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil
1637 .raw => return error.IncrFailed,1640 .raw => return error.IncrFailed,
1638 }1641 }
1639 var self: File.Elf = .{1642 var self: File.Elf = .{
1643 .base = .{
1644 .tag = .elf,
1645 .options = options,
1646 },
1640 .allocator = allocator,1647 .allocator = allocator,
1641 .file = file,1648 .file = file,
1642 .owns_file_handle = false,1649 .owns_file_handle = false,
1643 .options = options,
1644 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {1650 .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
1645 32 => .p32,1651 32 => .p32,
1646 64 => .p64,1652 64 => .p64,
src-self-hosted/type.zig+75-12
...@@ -380,7 +380,7 @@ pub const Type = extern union {...@@ -380,7 +380,7 @@ pub const Type = extern union {
380 },380 },
381 .single_mut_pointer => {381 .single_mut_pointer => {
382 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);382 const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);
383 try out_stream.writeAll("* ");383 try out_stream.writeAll("*");
384 ty = payload.pointee_type;384 ty = payload.pointee_type;
385 continue;385 continue;
386 },386 },
...@@ -803,6 +803,58 @@ pub const Type = extern union {...@@ -803,6 +803,58 @@ pub const Type = extern union {
803 };803 };
804 }804 }
805805
806 pub fn isVolatilePtr(self: Type) bool {
807 return switch (self.tag()) {
808 .u8,
809 .i8,
810 .u16,
811 .i16,
812 .u32,
813 .i32,
814 .u64,
815 .i64,
816 .usize,
817 .isize,
818 .c_short,
819 .c_ushort,
820 .c_int,
821 .c_uint,
822 .c_long,
823 .c_ulong,
824 .c_longlong,
825 .c_ulonglong,
826 .c_longdouble,
827 .f16,
828 .f32,
829 .f64,
830 .f128,
831 .c_void,
832 .bool,
833 .void,
834 .type,
835 .anyerror,
836 .comptime_int,
837 .comptime_float,
838 .noreturn,
839 .@"null",
840 .@"undefined",
841 .array,
842 .array_u8_sentinel_0,
843 .fn_noreturn_no_args,
844 .fn_void_no_args,
845 .fn_naked_noreturn_no_args,
846 .fn_ccc_void_no_args,
847 .function,
848 .int_unsigned,
849 .int_signed,
850 .single_mut_pointer,
851 .single_const_pointer,
852 .single_const_pointer_to_comptime_int,
853 .const_slice_u8,
854 => false,
855 };
856 }
857
806 /// Asserts the type is a pointer or array type.858 /// Asserts the type is a pointer or array type.
807 pub fn elemType(self: Type) Type {859 pub fn elemType(self: Type) Type {
808 return switch (self.tag()) {860 return switch (self.tag()) {
...@@ -1601,7 +1653,7 @@ pub const Type = extern union {...@@ -1601,7 +1653,7 @@ pub const Type = extern union {
1601 };1653 };
1602 }1654 }
16031655
1604 pub fn onePossibleValue(self: Type) bool {1656 pub fn onePossibleValue(self: Type) ?Value {
1605 var ty = self;1657 var ty = self;
1606 while (true) switch (ty.tag()) {1658 while (true) switch (ty.tag()) {
1607 .f16,1659 .f16,
...@@ -1640,21 +1692,32 @@ pub const Type = extern union {...@@ -1640,21 +1692,32 @@ pub const Type = extern union {
1640 .single_const_pointer_to_comptime_int,1692 .single_const_pointer_to_comptime_int,
1641 .array_u8_sentinel_0,1693 .array_u8_sentinel_0,
1642 .const_slice_u8,1694 .const_slice_u8,
1643 => return false,
1644
1645 .c_void,1695 .c_void,
1646 .void,1696 => return null,
1647 .noreturn,1697
1648 .@"null",1698 .void => return Value.initTag(.void_value),
1649 .@"undefined",1699 .noreturn => return Value.initTag(.unreachable_value),
1650 => return true,1700 .@"null" => return Value.initTag(.null_value),
1701 .@"undefined" => return Value.initTag(.undef),
16511702
1652 .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,1703 .int_unsigned => {
1653 .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,1704 if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
1705 return Value.initTag(.zero);
1706 } else {
1707 return null;
1708 }
1709 },
1710 .int_signed => {
1711 if (ty.cast(Payload.IntSigned).?.bits == 0) {
1712 return Value.initTag(.zero);
1713 } else {
1714 return null;
1715 }
1716 },
1654 .array => {1717 .array => {
1655 const array = ty.cast(Payload.Array).?;1718 const array = ty.cast(Payload.Array).?;
1656 if (array.len == 0)1719 if (array.len == 0)
1657 return true;1720 return Value.initTag(.empty_array);
1658 ty = array.elem_type;1721 ty = array.elem_type;
1659 continue;1722 continue;
1660 },1723 },
src-self-hosted/value.zig+50-22
...@@ -63,7 +63,9 @@ pub const Value = extern union {...@@ -63,7 +63,9 @@ pub const Value = extern union {
6363
64 undef,64 undef,
65 zero,65 zero,
66 the_one_possible_value, // when the type only has one possible value66 void_value,
67 unreachable_value,
68 empty_array,
67 null_value,69 null_value,
68 bool_true,70 bool_true,
69 bool_false, // See last_no_payload_tag below.71 bool_false, // See last_no_payload_tag below.
...@@ -164,7 +166,9 @@ pub const Value = extern union {...@@ -164,7 +166,9 @@ pub const Value = extern union {
164 .const_slice_u8_type,166 .const_slice_u8_type,
165 .undef,167 .undef,
166 .zero,168 .zero,
167 .the_one_possible_value,169 .void_value,
170 .unreachable_value,
171 .empty_array,
168 .null_value,172 .null_value,
169 .bool_true,173 .bool_true,
170 .bool_false,174 .bool_false,
...@@ -285,7 +289,8 @@ pub const Value = extern union {...@@ -285,7 +289,8 @@ pub const Value = extern union {
285 .null_value => return out_stream.writeAll("null"),289 .null_value => return out_stream.writeAll("null"),
286 .undef => return out_stream.writeAll("undefined"),290 .undef => return out_stream.writeAll("undefined"),
287 .zero => return out_stream.writeAll("0"),291 .zero => return out_stream.writeAll("0"),
288 .the_one_possible_value => return out_stream.writeAll("(one possible value)"),292 .void_value => return out_stream.writeAll("{}"),
293 .unreachable_value => return out_stream.writeAll("unreachable"),
289 .bool_true => return out_stream.writeAll("true"),294 .bool_true => return out_stream.writeAll("true"),
290 .bool_false => return out_stream.writeAll("false"),295 .bool_false => return out_stream.writeAll("false"),
291 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),296 .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
...@@ -312,6 +317,7 @@ pub const Value = extern union {...@@ -312,6 +317,7 @@ pub const Value = extern union {
312 try out_stream.print("&[{}] ", .{elem_ptr.index});317 try out_stream.print("&[{}] ", .{elem_ptr.index});
313 val = elem_ptr.array_ptr;318 val = elem_ptr.array_ptr;
314 },319 },
320 .empty_array => return out_stream.writeAll(".{}"),
315 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),321 .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
316 .repeated => {322 .repeated => {
317 try out_stream.writeAll("(repeated) ");323 try out_stream.writeAll("(repeated) ");
...@@ -388,7 +394,9 @@ pub const Value = extern union {...@@ -388,7 +394,9 @@ pub const Value = extern union {
388394
389 .undef,395 .undef,
390 .zero,396 .zero,
391 .the_one_possible_value,397 .void_value,
398 .unreachable_value,
399 .empty_array,
392 .bool_true,400 .bool_true,
393 .bool_false,401 .bool_false,
394 .null_value,402 .null_value,
...@@ -460,15 +468,18 @@ pub const Value = extern union {...@@ -460,15 +468,18 @@ pub const Value = extern union {
460 .decl_ref,468 .decl_ref,
461 .elem_ptr,469 .elem_ptr,
462 .bytes,470 .bytes,
463 .undef,
464 .repeated,471 .repeated,
465 .float_16,472 .float_16,
466 .float_32,473 .float_32,
467 .float_64,474 .float_64,
468 .float_128,475 .float_128,
476 .void_value,
477 .unreachable_value,
478 .empty_array,
469 => unreachable,479 => unreachable,
470480
471 .the_one_possible_value, // An integer with one possible value is always zero.481 .undef => unreachable,
482
472 .zero,483 .zero,
473 .bool_false,484 .bool_false,
474 => return BigIntMutable.init(&space.limbs, 0).toConst(),485 => return BigIntMutable.init(&space.limbs, 0).toConst(),
...@@ -532,16 +543,19 @@ pub const Value = extern union {...@@ -532,16 +543,19 @@ pub const Value = extern union {
532 .decl_ref,543 .decl_ref,
533 .elem_ptr,544 .elem_ptr,
534 .bytes,545 .bytes,
535 .undef,
536 .repeated,546 .repeated,
537 .float_16,547 .float_16,
538 .float_32,548 .float_32,
539 .float_64,549 .float_64,
540 .float_128,550 .float_128,
551 .void_value,
552 .unreachable_value,
553 .empty_array,
541 => unreachable,554 => unreachable,
542555
556 .undef => unreachable,
557
543 .zero,558 .zero,
544 .the_one_possible_value, // an integer with one possible value is always zero
545 .bool_false,559 .bool_false,
546 => return 0,560 => return 0,
547561
...@@ -570,7 +584,7 @@ pub const Value = extern union {...@@ -570,7 +584,7 @@ pub const Value = extern union {
570 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),584 .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
571 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),585 .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
572586
573 .zero, .the_one_possible_value => 0,587 .zero => 0,
574 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),588 .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
575 // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),589 // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
576 .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),590 .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
...@@ -637,9 +651,11 @@ pub const Value = extern union {...@@ -637,9 +651,11 @@ pub const Value = extern union {
637 .float_32,651 .float_32,
638 .float_64,652 .float_64,
639 .float_128,653 .float_128,
654 .void_value,
655 .unreachable_value,
656 .empty_array,
640 => unreachable,657 => unreachable,
641658
642 .the_one_possible_value, // an integer with one possible value is always zero
643 .zero,659 .zero,
644 .bool_false,660 .bool_false,
645 => return 0,661 => return 0,
...@@ -714,11 +730,13 @@ pub const Value = extern union {...@@ -714,11 +730,13 @@ pub const Value = extern union {
714 .float_32,730 .float_32,
715 .float_64,731 .float_64,
716 .float_128,732 .float_128,
733 .void_value,
734 .unreachable_value,
735 .empty_array,
717 => unreachable,736 => unreachable,
718737
719 .zero,738 .zero,
720 .undef,739 .undef,
721 .the_one_possible_value, // an integer with one possible value is always zero
722 .bool_false,740 .bool_false,
723 => return true,741 => return true,
724742
...@@ -797,13 +815,13 @@ pub const Value = extern union {...@@ -797,13 +815,13 @@ pub const Value = extern union {
797 // return Value.initPayload(&res_payload.base).copy(allocator);815 // return Value.initPayload(&res_payload.base).copy(allocator);
798 },816 },
799 32 => {817 32 => {
800 var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};818 var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
801 if (!self.eql(Value.initPayload(&res_payload.base)))819 if (!self.eql(Value.initPayload(&res_payload.base)))
802 return error.Overflow;820 return error.Overflow;
803 return Value.initPayload(&res_payload.base).copy(allocator);821 return Value.initPayload(&res_payload.base).copy(allocator);
804 },822 },
805 64 => {823 64 => {
806 var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};824 var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
807 if (!self.eql(Value.initPayload(&res_payload.base)))825 if (!self.eql(Value.initPayload(&res_payload.base)))
808 return error.Overflow;826 return error.Overflow;
809 return Value.initPayload(&res_payload.base).copy(allocator);827 return Value.initPayload(&res_payload.base).copy(allocator);
...@@ -875,7 +893,9 @@ pub const Value = extern union {...@@ -875,7 +893,9 @@ pub const Value = extern union {
875 .int_i64,893 .int_i64,
876 .int_big_positive,894 .int_big_positive,
877 .int_big_negative,895 .int_big_negative,
878 .the_one_possible_value,896 .empty_array,
897 .void_value,
898 .unreachable_value,
879 => unreachable,899 => unreachable,
880900
881 .zero => false,901 .zero => false,
...@@ -939,10 +959,12 @@ pub const Value = extern union {...@@ -939,10 +959,12 @@ pub const Value = extern union {
939 .bytes,959 .bytes,
940 .repeated,960 .repeated,
941 .undef,961 .undef,
962 .void_value,
963 .unreachable_value,
964 .empty_array,
942 => unreachable,965 => unreachable,
943966
944 .zero,967 .zero,
945 .the_one_possible_value, // an integer with one possible value is always zero
946 .bool_false,968 .bool_false,
947 => .eq,969 => .eq,
948970
...@@ -964,8 +986,8 @@ pub const Value = extern union {...@@ -964,8 +986,8 @@ pub const Value = extern union {
964 pub fn order(lhs: Value, rhs: Value) std.math.Order {986 pub fn order(lhs: Value, rhs: Value) std.math.Order {
965 const lhs_tag = lhs.tag();987 const lhs_tag = lhs.tag();
966 const rhs_tag = rhs.tag();988 const rhs_tag = rhs.tag();
967 const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;989 const lhs_is_zero = lhs_tag == .zero;
968 const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;990 const rhs_is_zero = rhs_tag == .zero;
969 if (lhs_is_zero) return rhs.orderAgainstZero().invert();991 if (lhs_is_zero) return rhs.orderAgainstZero().invert();
970 if (rhs_is_zero) return lhs.orderAgainstZero();992 if (rhs_is_zero) return lhs.orderAgainstZero();
971993
...@@ -1071,9 +1093,11 @@ pub const Value = extern union {...@@ -1071,9 +1093,11 @@ pub const Value = extern union {
1071 .float_32,1093 .float_32,
1072 .float_64,1094 .float_64,
1073 .float_128,1095 .float_128,
1096 .void_value,
1097 .unreachable_value,
1098 .empty_array,
1074 => unreachable,1099 => unreachable,
10751100
1076 .the_one_possible_value => Value.initTag(.the_one_possible_value),
1077 .ref_val => self.cast(Payload.RefVal).?.val,1101 .ref_val => self.cast(Payload.RefVal).?.val,
1078 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),1102 .decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
1079 .elem_ptr => {1103 .elem_ptr => {
...@@ -1130,7 +1154,6 @@ pub const Value = extern union {...@@ -1130,7 +1154,6 @@ pub const Value = extern union {
1130 .single_const_pointer_to_comptime_int_type,1154 .single_const_pointer_to_comptime_int_type,
1131 .const_slice_u8_type,1155 .const_slice_u8_type,
1132 .zero,1156 .zero,
1133 .the_one_possible_value,
1134 .bool_true,1157 .bool_true,
1135 .bool_false,1158 .bool_false,
1136 .null_value,1159 .null_value,
...@@ -1147,8 +1170,12 @@ pub const Value = extern union {...@@ -1147,8 +1170,12 @@ pub const Value = extern union {
1147 .float_32,1170 .float_32,
1148 .float_64,1171 .float_64,
1149 .float_128,1172 .float_128,
1173 .void_value,
1174 .unreachable_value,
1150 => unreachable,1175 => unreachable,
11511176
1177 .empty_array => unreachable, // out of bounds array index
1178
1152 .bytes => {1179 .bytes => {
1153 const int_payload = try allocator.create(Payload.Int_u64);1180 const int_payload = try allocator.create(Payload.Int_u64);
1154 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };1181 int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
...@@ -1175,8 +1202,7 @@ pub const Value = extern union {...@@ -1175,8 +1202,7 @@ pub const Value = extern union {
1175 return self.tag() == .undef;1202 return self.tag() == .undef;
1176 }1203 }
11771204
1178 /// Valid for all types. Asserts the value is not undefined.1205 /// Valid for all types. Asserts the value is not undefined and not unreachable.
1179 /// `.the_one_possible_value` is reported as not null.
1180 pub fn isNull(self: Value) bool {1206 pub fn isNull(self: Value) bool {
1181 return switch (self.tag()) {1207 return switch (self.tag()) {
1182 .ty,1208 .ty,
...@@ -1221,7 +1247,7 @@ pub const Value = extern union {...@@ -1221,7 +1247,7 @@ pub const Value = extern union {
1221 .single_const_pointer_to_comptime_int_type,1247 .single_const_pointer_to_comptime_int_type,
1222 .const_slice_u8_type,1248 .const_slice_u8_type,
1223 .zero,1249 .zero,
1224 .the_one_possible_value,1250 .empty_array,
1225 .bool_true,1251 .bool_true,
1226 .bool_false,1252 .bool_false,
1227 .function,1253 .function,
...@@ -1238,9 +1264,11 @@ pub const Value = extern union {...@@ -1238,9 +1264,11 @@ pub const Value = extern union {
1238 .float_32,1264 .float_32,
1239 .float_64,1265 .float_64,
1240 .float_128,1266 .float_128,
1267 .void_value,
1241 => false,1268 => false,
12421269
1243 .undef => unreachable,1270 .undef => unreachable,
1271 .unreachable_value => unreachable,
1244 .null_value => true,1272 .null_value => true,
1245 };1273 };
1246 }1274 }
src-self-hosted/zir.zig+47-13
...@@ -183,6 +183,10 @@ pub const Inst = struct {...@@ -183,6 +183,10 @@ pub const Inst = struct {
183 shl,183 shl,
184 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.184 /// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
185 shr,185 shr,
186 /// Create a const pointer type based on the element type. `*const T`
187 single_const_ptr_type,
188 /// Create a mutable pointer type based on the element type. `*T`
189 single_mut_ptr_type,
186 /// Write a value to a pointer. For loading, see `deref`.190 /// Write a value to a pointer. For loading, see `deref`.
187 store,191 store,
188 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.192 /// String Literal. Makes an anonymous Decl and then takes a pointer to it.
...@@ -228,6 +232,8 @@ pub const Inst = struct {...@@ -228,6 +232,8 @@ pub const Inst = struct {
228 .ref,232 .ref,
229 .bitcast_lvalue,233 .bitcast_lvalue,
230 .typeof,234 .typeof,
235 .single_const_ptr_type,
236 .single_mut_ptr_type,
231 => UnOp,237 => UnOp,
232238
233 .add,239 .add,
...@@ -242,6 +248,7 @@ pub const Inst = struct {...@@ -242,6 +248,7 @@ pub const Inst = struct {
242 .mulwrap,248 .mulwrap,
243 .shl,249 .shl,
244 .shr,250 .shr,
251 .store,
245 .sub,252 .sub,
246 .subwrap,253 .subwrap,
247 .cmp_lt,254 .cmp_lt,
...@@ -270,7 +277,6 @@ pub const Inst = struct {...@@ -270,7 +277,6 @@ pub const Inst = struct {
270 .coerce_result_block_ptr => CoerceResultBlockPtr,277 .coerce_result_block_ptr => CoerceResultBlockPtr,
271 .compileerror => CompileError,278 .compileerror => CompileError,
272 .@"const" => Const,279 .@"const" => Const,
273 .store => Store,
274 .str => Str,280 .str => Str,
275 .int => Int,281 .int => Int,
276 .inttype => IntType,282 .inttype => IntType,
...@@ -348,6 +354,8 @@ pub const Inst = struct {...@@ -348,6 +354,8 @@ pub const Inst = struct {
348 .ret_type,354 .ret_type,
349 .shl,355 .shl,
350 .shr,356 .shr,
357 .single_const_ptr_type,
358 .single_mut_ptr_type,
351 .store,359 .store,
352 .str,360 .str,
353 .sub,361 .sub,
...@@ -545,17 +553,6 @@ pub const Inst = struct {...@@ -545,17 +553,6 @@ pub const Inst = struct {
545 kw_args: struct {},553 kw_args: struct {},
546 };554 };
547555
548 pub const Store = struct {
549 pub const base_tag = Tag.store;
550 base: Inst,
551
552 positionals: struct {
553 ptr: *Inst,
554 value: *Inst,
555 },
556 kw_args: struct {},
557 };
558
559 pub const Str = struct {556 pub const Str = struct {
560 pub const base_tag = Tag.str;557 pub const base_tag = Tag.str;
561 base: Inst,558 base: Inst,
...@@ -745,7 +742,7 @@ pub const Inst = struct {...@@ -745,7 +742,7 @@ pub const Inst = struct {
745 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },742 .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
746 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },743 .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
747 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },744 .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
748 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },745 .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
749 };746 };
750 }747 }
751 };748 };
...@@ -1601,6 +1598,21 @@ const EmitZIR = struct {...@@ -1601,6 +1598,21 @@ const EmitZIR = struct {
1601 const decl = decl_ref.decl;1598 const decl = decl_ref.decl;
1602 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));1599 return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
1603 }1600 }
1601 if (typed_value.val.isUndef()) {
1602 const as_inst = try self.arena.allocator.create(Inst.BinOp);
1603 as_inst.* = .{
1604 .base = .{
1605 .tag = .as,
1606 .src = src,
1607 },
1608 .positionals = .{
1609 .lhs = (try self.emitType(src, typed_value.ty)).inst,
1610 .rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
1611 },
1612 .kw_args = .{},
1613 };
1614 return self.emitUnnamedDecl(&as_inst.base);
1615 }
1604 switch (typed_value.ty.zigTypeTag()) {1616 switch (typed_value.ty.zigTypeTag()) {
1605 .Pointer => {1617 .Pointer => {
1606 const ptr_elem_type = typed_value.ty.elemType();1618 const ptr_elem_type = typed_value.ty.elemType();
...@@ -1837,9 +1849,12 @@ const EmitZIR = struct {...@@ -1837,9 +1849,12 @@ const EmitZIR = struct {
1837 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),1849 .ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
1838 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),1850 .isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
1839 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),1851 .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
1852 .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
1853 .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
18401854
1841 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),1855 .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
1842 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),1856 .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
1857 .store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
1843 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),1858 .cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
1844 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),1859 .cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
1845 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),1860 .cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
...@@ -2103,6 +2118,25 @@ const EmitZIR = struct {...@@ -2103,6 +2118,25 @@ const EmitZIR = struct {
2103 };2118 };
2104 return self.emitUnnamedDecl(&inttype_inst.base);2119 return self.emitUnnamedDecl(&inttype_inst.base);
2105 },2120 },
2121 .Pointer => {
2122 if (ty.isSinglePointer()) {
2123 const inst = try self.arena.allocator.create(Inst.UnOp);
2124 const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type;
2125 inst.* = .{
2126 .base = .{
2127 .src = src,
2128 .tag = tag,
2129 },
2130 .positionals = .{
2131 .operand = (try self.emitType(src, ty.elemType())).inst,
2132 },
2133 .kw_args = .{},
2134 };
2135 return self.emitUnnamedDecl(&inst.base);
2136 } else {
2137 std.debug.panic("TODO implement emitType for {}", .{ty});
2138 }
2139 },
2106 else => std.debug.panic("TODO implement emitType for {}", .{ty}),2140 else => std.debug.panic("TODO implement emitType for {}", .{ty}),
2107 },2141 },
2108 }2142 }
src-self-hosted/zir_sema.zig+28-5
...@@ -50,6 +50,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!...@@ -50,6 +50,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
50 .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),50 .ref => return analyzeInstRef(mod, scope, old_inst.castTag(.ref).?),
51 .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),51 .ret_ptr => return analyzeInstRetPtr(mod, scope, old_inst.castTag(.ret_ptr).?),
52 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),52 .ret_type => return analyzeInstRetType(mod, scope, old_inst.castTag(.ret_type).?),
53 .single_const_ptr_type => return analyzeInstSingleConstPtrType(mod, scope, old_inst.castTag(.single_const_ptr_type).?),
54 .single_mut_ptr_type => return analyzeInstSingleMutPtrType(mod, scope, old_inst.castTag(.single_mut_ptr_type).?),
53 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),55 .store => return analyzeInstStore(mod, scope, old_inst.castTag(.store).?),
54 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),56 .str => return analyzeInstStr(mod, scope, old_inst.castTag(.str).?),
55 .int => {57 .int => {
...@@ -287,8 +289,11 @@ fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp...@@ -287,8 +289,11 @@ fn analyzeInstCoerceResultPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp
287 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});289 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceResultPtr", .{});
288}290}
289291
292/// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`.
290fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {293fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst {
291 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstCoerceToPtrElem", .{});294 const ptr = try resolveInst(mod, scope, inst.positionals.ptr);
295 const operand = try resolveInst(mod, scope, inst.positionals.value);
296 return mod.coerce(scope, ptr.ty.elemType(), operand);
292}297}
293298
294fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {299fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
...@@ -296,7 +301,10 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr...@@ -296,7 +301,10 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr
296}301}
297302
298fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {303fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
299 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstRef", .{});304 const operand = try resolveInst(mod, scope, inst.positionals.operand);
305 const b = try mod.requireRuntimeBlock(scope, inst.base.src);
306 const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty);
307 return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand);
300}308}
301309
302fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {310fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst {
...@@ -333,8 +341,10 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I...@@ -333,8 +341,10 @@ fn analyzeInstAllocInferred(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) I
333 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});341 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstAllocInferred", .{});
334}342}
335343
336fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst {344fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*Inst {
337 return mod.fail(scope, inst.base.src, "TODO implement analyzeInstStore", .{});345 const ptr = try resolveInst(mod, scope, inst.positionals.lhs);
346 const value = try resolveInst(mod, scope, inst.positionals.rhs);
347 return mod.storePtr(scope, inst.base.src, ptr, value);
338}348}
339349
340fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {350fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst {
...@@ -872,7 +882,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn...@@ -872,7 +882,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
872fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {882fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
873 // incase rhs is 0, simply return lhs without doing any calculations883 // incase rhs is 0, simply return lhs without doing any calculations
874 // TODO Once division is implemented we should throw an error when dividing by 0.884 // TODO Once division is implemented we should throw an error when dividing by 0.
875 if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) {885 if (rhs_val.compareWithZero(.eq)) {
876 return mod.constInst(scope, inst.base.src, .{886 return mod.constInst(scope, inst.base.src, .{
877 .ty = res_type,887 .ty = res_type,
878 .val = lhs_val,888 .val = lhs_val,
...@@ -1073,6 +1083,7 @@ fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp)...@@ -1073,6 +1083,7 @@ fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp)
10731083
1074fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1084fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
1075 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);1085 const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
1086 // TODO Add compile error for @optimizeFor occurring too late in a scope.
1076 if (mod.wantSafety(scope)) {1087 if (mod.wantSafety(scope)) {
1077 // TODO Once we have a panic function to call, call it here instead of this.1088 // TODO Once we have a panic function to call, call it here instead of this.
1078 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);1089 _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
...@@ -1129,3 +1140,15 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr...@@ -1129,3 +1140,15 @@ fn analyzeDeclVal(mod: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerErr
11291140
1130 return decl;1141 return decl;
1131}1142}
1143
1144fn analyzeInstSingleConstPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1145 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1146 const ty = try mod.singleConstPtrType(scope, inst.base.src, elem_type);
1147 return mod.constType(scope, inst.base.src, ty);
1148}
1149
1150fn analyzeInstSingleMutPtrType(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1151 const elem_type = try resolveType(mod, scope, inst.positionals.operand);
1152 const ty = try mod.singleMutPtrType(scope, inst.base.src, elem_type);
1153 return mod.constType(scope, inst.base.src, ty);
1154}
test/stage2/compare_output.zig+34
...@@ -363,5 +363,39 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -363,5 +363,39 @@ pub fn addCases(ctx: *TestContext) !void {
363 ,363 ,
364 "",364 "",
365 );365 );
366
367 // Local mutable variables.
368 case.addCompareOutput(
369 \\export fn _start() noreturn {
370 \\ assert(add(3, 4) == 7);
371 \\ assert(add(20, 10) == 30);
372 \\
373 \\ exit();
374 \\}
375 \\
376 \\fn add(a: u32, b: u32) u32 {
377 \\ var x: u32 = undefined;
378 \\ x = 0;
379 \\ x += a;
380 \\ x += b;
381 \\ return x;
382 \\}
383 \\
384 \\pub fn assert(ok: bool) void {
385 \\ if (!ok) unreachable; // assertion failure
386 \\}
387 \\
388 \\fn exit() noreturn {
389 \\ asm volatile ("syscall"
390 \\ :
391 \\ : [number] "{rax}" (231),
392 \\ [arg1] "{rdi}" (0)
393 \\ : "rcx", "r11", "memory"
394 \\ );
395 \\ unreachable;
396 \\}
397 ,
398 "",
399 );
366 }400 }
367}401}