| author | |
| committer | |
| log | b3b00ec62f518875a486b4da532f74e304c3aba2 |
| tree | 5ae6f6dd8afba7cacd3d5b1532d4c25b3661189d |
| parent | 4fdfaf69c8c55ebac4c5b3c00025c0ac51281b5b |
| parent | 8899e6e334758f2e101399075d0456195035c372 |
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); | ||
| 91 | 93 | ||
| 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. |
| 48 | decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, | 48 | decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, |
| 49 | 49 | ||
| 50 | optimize_mode: std.builtin.Mode, | ||
| 51 | link_error_flags: link.File.ErrorFlags = .{}, | 50 | link_error_flags: link.File.ErrorFlags = .{}, |
| 52 | 51 | ||
| 53 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | 52 | work_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 | } |
| 387 | 386 | ||
| 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 and | 387 | /// 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(); |
| 807 | 795 | ||
| ... | @@ -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 | } |
| 895 | 882 | ||
| 896 | pub fn target(self: Module) std.Target { | 883 | pub fn target(self: Module) std.Target { |
| 897 | return self.bin_file.options().target; | 884 | return self.bin_file.options.target; |
| 885 | } | ||
| 886 | |||
| 887 | pub fn optimizeMode(self: Module) std.builtin.Mode { | ||
| 888 | return self.bin_file.options.optimize_mode; | ||
| 898 | } | 889 | } |
| 899 | 890 | ||
| 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 { |
| 1991 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { | 1982 | pub 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 | } |
| 1997 | 1988 | ||
| 1998 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { | 1989 | pub 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 | } |
| 2004 | 1995 | ||
| ... | @@ -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 | } |
| 2153 | 2144 | ||
| 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 | } |
| 2156 | 2148 | ||
| 2157 | pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: []const u8) InnerError!*Inst { | 2149 | pub 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 | } |
| 2162 | 2154 | ||
| 2163 | pub fn wantSafety(self: *Module, scope: *Scope) bool { | 2155 | pub 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 | } |
| 2506 | 2499 | ||
| 2500 | pub 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 | |||
| 2507 | pub fn bitcast(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | 2516 | pub 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 | |||
| 2793 | pub 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 | |||
| 2799 | pub 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), |
| 89 | 89 | ||
| 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 |
| 145 | 145 | ||
| 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 | } |
| 471 | 473 | ||
| 472 | fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst { | 474 | fn 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(); |
| 475 | 477 | ||
| ... | @@ -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 | } |
| 482 | 484 | ||
| 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 | } |
| 486 | 489 | ||
| 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 | } |
| 520 | 525 | ||
| ... | @@ -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 | } |
| 543 | 553 | ||
| 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 | } |
| 547 | 559 | ||
| 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( |
| 50 | 50 | ||
| 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, |
| 215 | 216 | ||
| 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, |
| 275 | 294 | ||
| 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 | } |
| 311 | 330 | ||
| 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 { |
| 349 | 381 | ||
| 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); | ||
| 364 | 398 | ||
| 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 | } |
| 387 | 421 | ||
| 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) { |
| 390 | 424 | .x86_64 => { | |
| 391 | // TODO omit this for naked functions | 425 | try self.code.ensureCapacity(self.code.items.len + 11); |
| 392 | // push rbp | 426 | |
| 393 | // mov rbp, rsp | 427 | const cc = self.fn_type.fnCallingConvention(); |
| 394 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 }); | 428 | if (cc != .Naked) { |
| 395 | 429 | // We want to subtract the aligned stack frame size from rsp here, but we don't | |
| 396 | // sub rsp, x | 430 | // 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,0x10 | 435 | 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,0x80 | 440 | // 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 | } | ||
| 410 | 482 | ||
| 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 | } |
| 413 | 496 | ||
| 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 death | 522 | 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 bodies | 545 | .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 | } |
| 474 | 561 | ||
| 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 | } |
| 496 | 629 | ||
| 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 | } |
| 574 | 707 | ||
| 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], x | 976 | // 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 | } |
| 790 | 1019 | ||
| 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); // ret | 1052 | 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 rbp | 1056 | // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction |
| 800 | 0xc3, // ret | 1057 | // 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, 1 | 1141 | // test reg, 1 |
| 883 | // TODO detect al, ax, eax | 1142 | // 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) catch | 1207 | 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 | } |
| 1043 | 1309 | ||
| 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 | } |
| 1055 | 1322 | ||
| 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-bit | 1461 | // 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 0b0100100B | 1511 | // 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 three | 1524 | // 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; |
| 1165 | 1541 | ||
| 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 middle | 1546 | // 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), // R | 1566 | 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 with | 1588 | // 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); |
| 1222 | 1592 | ||
| 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 | } |
| 1281 | 1671 | ||
| 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 track | 1674 | /// 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 | } |
| 1325 | 1697 | ||
| 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 | } |
| 1437 | 1818 | ||
| 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 | }; |
| 85 | 105 | ||
| 86 | // zig fmt: on | 106 | // 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, |
| 87 | 92 | ||
| 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, |
| 97 | 104 | ||
| 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, |
| 107 | 115 | ||
| 108 | .assembly => Assembly, | 116 | .assembly => Assembly, |
| ... | @@ -157,8 +165,7 @@ pub const Inst = struct { | ... | @@ -157,8 +165,7 @@ pub const Inst = struct { |
| 157 | 165 | ||
| 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); | ||
| 162 | 169 | ||
| 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 file | 20 | /// 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( |
| 88 | 90 | ||
| 89 | fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C { | 91 | fn 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 |
| 114 | 119 | ||
| 115 | pub const File = struct { | 120 | pub 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 { |
| 123 | 130 | ||
| 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 | } |
| 130 | 137 | ||
| 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 | } |
| 137 | 144 | ||
| 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 | } |
| 144 | 151 | ||
| 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 | } |
| 151 | 158 | ||
| 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 | } |
| 158 | 165 | ||
| 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 { |
| 173 | 180 | ||
| 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 | } |
| 180 | 187 | ||
| 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 | } |
| 187 | 194 | ||
| 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 | } |
| 201 | 201 | ||
| ... | @@ -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 | } |
| 214 | 214 | ||
| 215 | pub const Tag = enum { | 215 | pub const Tag = enum { |
| 216 | Elf, | 216 | elf, |
| 217 | C, | 217 | c, |
| 218 | }; | 218 | }; |
| 219 | 219 | ||
| 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 | }; |
| 223 | 223 | ||
| 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, | ||
| 227 | 228 | ||
| 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 | }; |
| 295 | 295 | ||
| 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, | ||
| 299 | 300 | ||
| 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 }, |
| 305 | 305 | ||
| 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 | } |
| 466 | 466 | ||
| 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 requires | 592 | // 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 }); |
| 677 | 677 | ||
| ... | @@ -726,7 +726,7 @@ pub const File = struct { | ... | @@ -726,7 +726,7 @@ pub const File = struct { |
| 726 | 726 | ||
| 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(); |
| 730 | 730 | ||
| 731 | // Unfortunately these have to be buffered and done at the end because ELF does not allow | 731 | // 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; |
| 877 | 877 | ||
| 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 { |
| 893 | 893 | ||
| 894 | assert(index == 16); | 894 | assert(index == 16); |
| 895 | 895 | ||
| 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; |
| 906 | 906 | ||
| 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; |
| 910 | 910 | ||
| ... | @@ -1216,7 +1216,7 @@ pub const File = struct { | ... | @@ -1216,7 +1216,7 @@ pub const File = struct { |
| 1216 | }, | 1216 | }, |
| 1217 | }; | 1217 | }; |
| 1218 | 1218 | ||
| 1219 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); | 1219 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 1220 | 1220 | ||
| 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 | } |
| 1362 | 1362 | ||
| 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 | } |
| 1384 | 1384 | ||
| 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 { |
| 1433 | 1433 | ||
| 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 later | 1475 | syms_sect.sh_size = needed_size; // anticipating adding the global symbols later |
| 1476 | self.shdr_table_dirty = true; // TODO look into only writing one section | 1476 | 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 | } |
| 1578 | 1578 | ||
| 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 | } |
| 805 | 805 | ||
| 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 | } |
| 1603 | 1655 | ||
| 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), | ||
| 1651 | 1702 | ||
| 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 { |
| 63 | 63 | ||
| 64 | undef, | 64 | undef, |
| 65 | zero, | 65 | zero, |
| 66 | the_one_possible_value, // when the type only has one possible value | 66 | 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 { |
| 388 | 394 | ||
| 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, |
| 470 | 480 | ||
| 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, |
| 542 | 555 | ||
| 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, |
| 547 | 561 | ||
| ... | @@ -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), |
| 572 | 586 | ||
| 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, |
| 641 | 658 | ||
| 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, |
| 718 | 737 | ||
| 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, |
| 724 | 742 | ||
| ... | @@ -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, |
| 880 | 900 | ||
| 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, |
| 943 | 966 | ||
| 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, |
| 948 | 970 | ||
| ... | @@ -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(); |
| 971 | 993 | ||
| ... | @@ -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, |
| 1075 | 1100 | ||
| 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, |
| 1151 | 1176 | ||
| 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 | } |
| 1177 | 1204 | ||
| 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, |
| 1242 | 1269 | ||
| 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, |
| 232 | 238 | ||
| 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 | }; |
| 547 | 555 | ||
| 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), | ||
| 1840 | 1854 | ||
| 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 | } |
| 289 | 291 | ||
| 292 | /// Equivalent to `as(ptr_child_type(typeof(ptr)), value)`. | ||
| 290 | fn analyzeInstCoerceToPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.CoerceToPtrElem) InnerError!*Inst { | 293 | fn 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 | } |
| 293 | 298 | ||
| 294 | fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 299 | fn 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 | } |
| 297 | 302 | ||
| 298 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 303 | fn 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 | } |
| 301 | 309 | ||
| 302 | fn analyzeInstRetType(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { | 310 | fn 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 | } |
| 335 | 343 | ||
| 336 | fn analyzeInstStore(mod: *Module, scope: *Scope, inst: *zir.Inst.Store) InnerError!*Inst { | 344 | fn 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 | } |
| 339 | 349 | ||
| 340 | fn analyzeInstParamType(mod: *Module, scope: *Scope, inst: *zir.Inst.ParamType) InnerError!*Inst { | 350 | fn 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 |
| 872 | fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | 882 | fn 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 calculations | 883 | // 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) |
| 1073 | 1083 | ||
| 1074 | fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | 1084 | fn 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 |
| 1129 | 1140 | ||
| 1130 | return decl; | 1141 | return decl; |
| 1131 | } | 1142 | } |
| 1143 | |||
| 1144 | fn 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 | |||
| 1150 | fn 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 | } |