| author | |
| committer | |
| log | 2dd5e8b6f865a24498da06a9a0ce3609d23662c9 |
| tree | 96de992b22f7cfc4bcb672892c64fb2cdf621913 |
| parent | e1375942e584d4fffd3ecd06277ced7cf35d7e6c |
| parent | d5100dc81555f0e8197d5f189b1432070e8d72dd |
| signature |
stage2: tuple/slice mul/cat17 files changed, 331 insertions(+), 70 deletions(-)
lib/std/hash_map.zig+2-2| ... | @@ -738,7 +738,7 @@ pub fn HashMapUnmanaged( | ... | @@ -738,7 +738,7 @@ pub fn HashMapUnmanaged( |
| 738 | value: V, | 738 | value: V, |
| 739 | }; | 739 | }; |
| 740 | 740 | ||
| 741 | const Header = packed struct { | 741 | const Header = struct { |
| 742 | values: [*]V, | 742 | values: [*]V, |
| 743 | keys: [*]K, | 743 | keys: [*]K, |
| 744 | capacity: Size, | 744 | capacity: Size, |
| ... | @@ -932,7 +932,7 @@ pub fn HashMapUnmanaged( | ... | @@ -932,7 +932,7 @@ pub fn HashMapUnmanaged( |
| 932 | } | 932 | } |
| 933 | 933 | ||
| 934 | fn header(self: *const Self) *Header { | 934 | fn header(self: *const Self) *Header { |
| 935 | return @ptrCast(*Header, @ptrCast([*]Header, self.metadata.?) - 1); | 935 | return @ptrCast(*Header, @ptrCast([*]Header, @alignCast(@alignOf(Header), self.metadata.?)) - 1); |
| 936 | } | 936 | } |
| 937 | 937 | ||
| 938 | fn keys(self: *const Self) [*]K { | 938 | fn keys(self: *const Self) [*]K { |
lib/std/heap/general_purpose_allocator.zig+19-6| ... | @@ -341,9 +341,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -341,9 +341,15 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 341 | const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index); | 341 | const slot_index = @intCast(SlotIndex, used_bits_byte * 8 + bit_index); |
| 342 | const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc); | 342 | const stack_trace = bucketStackTrace(bucket, size_class, slot_index, .alloc); |
| 343 | const addr = bucket.page + slot_index * size_class; | 343 | const addr = bucket.page + slot_index * size_class; |
| 344 | log.err("memory address 0x{x} leaked: {s}", .{ | 344 | if (builtin.zig_backend == .stage1) { |
| 345 | @ptrToInt(addr), stack_trace, | 345 | log.err("memory address 0x{x} leaked: {s}", .{ |
| 346 | }); | 346 | @ptrToInt(addr), stack_trace, |
| 347 | }); | ||
| 348 | } else { // TODO | ||
| 349 | log.err("memory address 0x{x} leaked", .{ | ||
| 350 | @ptrToInt(addr), | ||
| 351 | }); | ||
| 352 | } | ||
| 347 | leaks = true; | 353 | leaks = true; |
| 348 | } | 354 | } |
| 349 | if (bit_index == math.maxInt(u3)) | 355 | if (bit_index == math.maxInt(u3)) |
| ... | @@ -372,9 +378,16 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { | ... | @@ -372,9 +378,16 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type { |
| 372 | var it = self.large_allocations.valueIterator(); | 378 | var it = self.large_allocations.valueIterator(); |
| 373 | while (it.next()) |large_alloc| { | 379 | while (it.next()) |large_alloc| { |
| 374 | if (config.retain_metadata and large_alloc.freed) continue; | 380 | if (config.retain_metadata and large_alloc.freed) continue; |
| 375 | log.err("memory address 0x{x} leaked: {s}", .{ | 381 | const stack_trace = large_alloc.getStackTrace(.alloc); |
| 376 | @ptrToInt(large_alloc.bytes.ptr), large_alloc.getStackTrace(.alloc), | 382 | if (builtin.zig_backend == .stage1) { |
| 377 | }); | 383 | log.err("memory address 0x{x} leaked: {s}", .{ |
| 384 | @ptrToInt(large_alloc.bytes.ptr), stack_trace, | ||
| 385 | }); | ||
| 386 | } else { // TODO | ||
| 387 | log.err("memory address 0x{x} leaked", .{ | ||
| 388 | @ptrToInt(large_alloc.bytes.ptr), | ||
| 389 | }); | ||
| 390 | } | ||
| 378 | leaks = true; | 391 | leaks = true; |
| 379 | } | 392 | } |
| 380 | return leaks; | 393 | return leaks; |
lib/std/special/test_runner.zig+3-2| ... | @@ -46,9 +46,10 @@ pub fn main() void { | ... | @@ -46,9 +46,10 @@ pub fn main() void { |
| 46 | 46 | ||
| 47 | var leaks: usize = 0; | 47 | var leaks: usize = 0; |
| 48 | for (test_fn_list) |test_fn, i| { | 48 | for (test_fn_list) |test_fn, i| { |
| 49 | if (builtin.zig_backend != .stage2_llvm) std.testing.allocator_instance = .{}; | 49 | const gpa_works = builtin.zig_backend == .stage1 or builtin.os.tag != .macos; |
| 50 | if (gpa_works) std.testing.allocator_instance = .{}; | ||
| 50 | defer { | 51 | defer { |
| 51 | if (builtin.zig_backend != .stage2_llvm and std.testing.allocator_instance.deinit()) { | 52 | if (gpa_works and std.testing.allocator_instance.deinit()) { |
| 52 | leaks += 1; | 53 | leaks += 1; |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
src/Air.zig+4| ... | @@ -218,6 +218,9 @@ pub const Inst = struct { | ... | @@ -218,6 +218,9 @@ pub const Inst = struct { |
| 218 | /// Yields the return address of the current function. | 218 | /// Yields the return address of the current function. |
| 219 | /// Uses the `no_op` field. | 219 | /// Uses the `no_op` field. |
| 220 | ret_addr, | 220 | ret_addr, |
| 221 | /// Implements @frameAddress builtin. | ||
| 222 | /// Uses the `no_op` field. | ||
| 223 | frame_addr, | ||
| 221 | /// Function call. | 224 | /// Function call. |
| 222 | /// Result type is the return type of the function being called. | 225 | /// Result type is the return type of the function being called. |
| 223 | /// Uses the `pl_op` field with the `Call` payload. operand is the callee. | 226 | /// Uses the `pl_op` field with the `Call` payload. operand is the callee. |
| ... | @@ -939,6 +942,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { | ... | @@ -939,6 +942,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type { |
| 939 | .ptrtoint, | 942 | .ptrtoint, |
| 940 | .slice_len, | 943 | .slice_len, |
| 941 | .ret_addr, | 944 | .ret_addr, |
| 945 | .frame_addr, | ||
| 942 | => return Type.initTag(.usize), | 946 | => return Type.initTag(.usize), |
| 943 | 947 | ||
| 944 | .bool_to_int => return Type.initTag(.u1), | 948 | .bool_to_int => return Type.initTag(.u1), |
src/Liveness.zig+1| ... | @@ -317,6 +317,7 @@ fn analyzeInst( | ... | @@ -317,6 +317,7 @@ fn analyzeInst( |
| 317 | .unreach, | 317 | .unreach, |
| 318 | .fence, | 318 | .fence, |
| 319 | .ret_addr, | 319 | .ret_addr, |
| 320 | .frame_addr, | ||
| 320 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), | 321 | => return trackOperands(a, new_set, inst, main_tomb, .{ .none, .none, .none }), |
| 321 | 322 | ||
| 322 | .not, | 323 | .not, |
src/Sema.zig+187-22| ... | @@ -8059,6 +8059,79 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -8059,6 +8059,79 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 8059 | return block.addTyOp(.not, operand_type, operand); | 8059 | return block.addTyOp(.not, operand_type, operand); |
| 8060 | } | 8060 | } |
| 8061 | 8061 | ||
| 8062 | fn analyzeTupleCat( | ||
| 8063 | sema: *Sema, | ||
| 8064 | block: *Block, | ||
| 8065 | src_node: i32, | ||
| 8066 | lhs: Air.Inst.Ref, | ||
| 8067 | rhs: Air.Inst.Ref, | ||
| 8068 | ) CompileError!Air.Inst.Ref { | ||
| 8069 | const lhs_ty = sema.typeOf(lhs); | ||
| 8070 | const rhs_ty = sema.typeOf(rhs); | ||
| 8071 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; | ||
| 8072 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; | ||
| 8073 | |||
| 8074 | const lhs_tuple = lhs_ty.tupleFields(); | ||
| 8075 | const rhs_tuple = rhs_ty.tupleFields(); | ||
| 8076 | const dest_fields = lhs_tuple.types.len + rhs_tuple.types.len; | ||
| 8077 | |||
| 8078 | if (dest_fields == 0) { | ||
| 8079 | return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value)); | ||
| 8080 | } | ||
| 8081 | const final_len = try sema.usizeCast(block, rhs_src, dest_fields); | ||
| 8082 | |||
| 8083 | const types = try sema.arena.alloc(Type, final_len); | ||
| 8084 | const values = try sema.arena.alloc(Value, final_len); | ||
| 8085 | |||
| 8086 | const opt_runtime_src = rs: { | ||
| 8087 | var runtime_src: ?LazySrcLoc = null; | ||
| 8088 | for (lhs_tuple.types) |ty, i| { | ||
| 8089 | types[i] = ty; | ||
| 8090 | values[i] = lhs_tuple.values[i]; | ||
| 8091 | const operand_src = lhs_src; // TODO better source location | ||
| 8092 | if (values[i].tag() == .unreachable_value) { | ||
| 8093 | runtime_src = operand_src; | ||
| 8094 | } | ||
| 8095 | } | ||
| 8096 | const offset = lhs_tuple.types.len; | ||
| 8097 | for (rhs_tuple.types) |ty, i| { | ||
| 8098 | types[i + offset] = ty; | ||
| 8099 | values[i + offset] = rhs_tuple.values[i]; | ||
| 8100 | const operand_src = rhs_src; // TODO better source location | ||
| 8101 | if (rhs_tuple.values[i].tag() == .unreachable_value) { | ||
| 8102 | runtime_src = operand_src; | ||
| 8103 | } | ||
| 8104 | } | ||
| 8105 | break :rs runtime_src; | ||
| 8106 | }; | ||
| 8107 | |||
| 8108 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ | ||
| 8109 | .types = types, | ||
| 8110 | .values = values, | ||
| 8111 | }); | ||
| 8112 | |||
| 8113 | const runtime_src = opt_runtime_src orelse { | ||
| 8114 | const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); | ||
| 8115 | return sema.addConstant(tuple_ty, tuple_val); | ||
| 8116 | }; | ||
| 8117 | |||
| 8118 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 8119 | |||
| 8120 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); | ||
| 8121 | for (lhs_tuple.types) |_, i| { | ||
| 8122 | const operand_src = lhs_src; // TODO better source location | ||
| 8123 | element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, lhs, @intCast(u32, i), lhs_ty); | ||
| 8124 | } | ||
| 8125 | const offset = lhs_tuple.types.len; | ||
| 8126 | for (rhs_tuple.types) |_, i| { | ||
| 8127 | const operand_src = rhs_src; // TODO better source location | ||
| 8128 | element_refs[i + offset] = | ||
| 8129 | try sema.tupleFieldValByIndex(block, operand_src, rhs, @intCast(u32, i), rhs_ty); | ||
| 8130 | } | ||
| 8131 | |||
| 8132 | return block.addAggregateInit(tuple_ty, element_refs); | ||
| 8133 | } | ||
| 8134 | |||
| 8062 | fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8135 | fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8063 | const tracy = trace(@src()); | 8136 | const tracy = trace(@src()); |
| 8064 | defer tracy.end(); | 8137 | defer tracy.end(); |
| ... | @@ -8069,12 +8142,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8069,12 +8142,17 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8069 | const rhs = sema.resolveInst(extra.rhs); | 8142 | const rhs = sema.resolveInst(extra.rhs); |
| 8070 | const lhs_ty = sema.typeOf(lhs); | 8143 | const lhs_ty = sema.typeOf(lhs); |
| 8071 | const rhs_ty = sema.typeOf(rhs); | 8144 | const rhs_ty = sema.typeOf(rhs); |
| 8145 | |||
| 8146 | if (lhs_ty.isTuple() and rhs_ty.isTuple()) { | ||
| 8147 | return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); | ||
| 8148 | } | ||
| 8149 | |||
| 8072 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; | 8150 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node }; |
| 8073 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; | 8151 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node }; |
| 8074 | 8152 | ||
| 8075 | const lhs_info = getArrayCatInfo(lhs_ty) orelse | 8153 | const lhs_info = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse |
| 8076 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); | 8154 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); |
| 8077 | const rhs_info = getArrayCatInfo(rhs_ty) orelse | 8155 | const rhs_info = (try sema.getArrayCatInfo(block, rhs_src, rhs)) orelse |
| 8078 | return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty}); | 8156 | return sema.fail(block, rhs_src, "expected array, found '{}'", .{rhs_ty}); |
| 8079 | if (!lhs_info.elem_type.eql(rhs_info.elem_type)) { | 8157 | if (!lhs_info.elem_type.eql(rhs_info.elem_type)) { |
| 8080 | return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty }); | 8158 | return sema.fail(block, rhs_src, "expected array of type '{}', found '{}'", .{ lhs_info.elem_type, rhs_ty }); |
| ... | @@ -8095,9 +8173,10 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8095,9 +8173,10 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8095 | const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); | 8173 | const rhs_len = try sema.usizeCast(block, lhs_src, rhs_info.len); |
| 8096 | const final_len = lhs_len + rhs_len; | 8174 | const final_len = lhs_len + rhs_len; |
| 8097 | const final_len_including_sent = final_len + @boolToInt(res_sent != null); | 8175 | const final_len_including_sent = final_len + @boolToInt(res_sent != null); |
| 8098 | const is_pointer = lhs_ty.zigTypeTag() == .Pointer; | 8176 | const lhs_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice(); |
| 8099 | const lhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; | 8177 | const rhs_single_ptr = rhs_ty.zigTypeTag() == .Pointer and !rhs_ty.isSlice(); |
| 8100 | const rhs_sub_val = if (is_pointer) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; | 8178 | const lhs_sub_val = if (lhs_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; |
| 8179 | const rhs_sub_val = if (rhs_single_ptr) (try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty)).? else rhs_val; | ||
| 8101 | var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); | 8180 | var anon_decl = try block.startAnonDecl(LazySrcLoc.unneeded); |
| 8102 | defer anon_decl.deinit(); | 8181 | defer anon_decl.deinit(); |
| 8103 | 8182 | ||
| ... | @@ -8129,7 +8208,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8129,7 +8208,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8129 | }); | 8208 | }); |
| 8130 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); | 8209 | const val = try Value.Tag.array.create(anon_decl.arena(), buf); |
| 8131 | const decl = try anon_decl.finish(ty, val); | 8210 | const decl = try anon_decl.finish(ty, val); |
| 8132 | if (is_pointer) { | 8211 | if (lhs_single_ptr or rhs_single_ptr) { |
| 8133 | return sema.analyzeDeclRef(decl); | 8212 | return sema.analyzeDeclRef(decl); |
| 8134 | } else { | 8213 | } else { |
| 8135 | return sema.analyzeDeclVal(block, .unneeded, decl); | 8214 | return sema.analyzeDeclVal(block, .unneeded, decl); |
| ... | @@ -8142,11 +8221,20 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8142,11 +8221,20 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8142 | } | 8221 | } |
| 8143 | } | 8222 | } |
| 8144 | 8223 | ||
| 8145 | fn getArrayCatInfo(t: Type) ?Type.ArrayInfo { | 8224 | fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, inst: Air.Inst.Ref) !?Type.ArrayInfo { |
| 8225 | const t = sema.typeOf(inst); | ||
| 8146 | return switch (t.zigTypeTag()) { | 8226 | return switch (t.zigTypeTag()) { |
| 8147 | .Array => t.arrayInfo(), | 8227 | .Array => t.arrayInfo(), |
| 8148 | .Pointer => blk: { | 8228 | .Pointer => blk: { |
| 8149 | const ptrinfo = t.ptrInfo().data; | 8229 | const ptrinfo = t.ptrInfo().data; |
| 8230 | if (ptrinfo.size == .Slice) { | ||
| 8231 | const val = try sema.resolveConstValue(block, src, inst); | ||
| 8232 | return Type.ArrayInfo{ | ||
| 8233 | .elem_type = t.childType(), | ||
| 8234 | .sentinel = t.sentinel(), | ||
| 8235 | .len = val.sliceLen(), | ||
| 8236 | }; | ||
| 8237 | } | ||
| 8150 | if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; | 8238 | if (ptrinfo.pointee_type.zigTypeTag() != .Array) return null; |
| 8151 | if (ptrinfo.size != .One) return null; | 8239 | if (ptrinfo.size != .One) return null; |
| 8152 | break :blk ptrinfo.pointee_type.arrayInfo(); | 8240 | break :blk ptrinfo.pointee_type.arrayInfo(); |
| ... | @@ -8155,6 +8243,73 @@ fn getArrayCatInfo(t: Type) ?Type.ArrayInfo { | ... | @@ -8155,6 +8243,73 @@ fn getArrayCatInfo(t: Type) ?Type.ArrayInfo { |
| 8155 | }; | 8243 | }; |
| 8156 | } | 8244 | } |
| 8157 | 8245 | ||
| 8246 | fn analyzeTupleMul( | ||
| 8247 | sema: *Sema, | ||
| 8248 | block: *Block, | ||
| 8249 | src_node: i32, | ||
| 8250 | operand: Air.Inst.Ref, | ||
| 8251 | factor: u64, | ||
| 8252 | ) CompileError!Air.Inst.Ref { | ||
| 8253 | const operand_ty = sema.typeOf(operand); | ||
| 8254 | const operand_tuple = operand_ty.tupleFields(); | ||
| 8255 | const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = src_node }; | ||
| 8256 | const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = src_node }; | ||
| 8257 | |||
| 8258 | const tuple_len = operand_tuple.types.len; | ||
| 8259 | const final_len_u64 = std.math.mul(u64, tuple_len, factor) catch | ||
| 8260 | return sema.fail(block, rhs_src, "operation results in overflow", .{}); | ||
| 8261 | |||
| 8262 | if (final_len_u64 == 0) { | ||
| 8263 | return sema.addConstant(Type.initTag(.empty_struct_literal), Value.initTag(.empty_struct_value)); | ||
| 8264 | } | ||
| 8265 | const final_len = try sema.usizeCast(block, rhs_src, final_len_u64); | ||
| 8266 | |||
| 8267 | const types = try sema.arena.alloc(Type, final_len); | ||
| 8268 | const values = try sema.arena.alloc(Value, final_len); | ||
| 8269 | |||
| 8270 | const opt_runtime_src = rs: { | ||
| 8271 | var runtime_src: ?LazySrcLoc = null; | ||
| 8272 | for (operand_tuple.types) |ty, i| { | ||
| 8273 | types[i] = ty; | ||
| 8274 | values[i] = operand_tuple.values[i]; | ||
| 8275 | const operand_src = lhs_src; // TODO better source location | ||
| 8276 | if (values[i].tag() == .unreachable_value) { | ||
| 8277 | runtime_src = operand_src; | ||
| 8278 | } | ||
| 8279 | } | ||
| 8280 | var i: usize = 1; | ||
| 8281 | while (i < factor) : (i += 1) { | ||
| 8282 | mem.copy(Type, types[tuple_len * i ..], operand_tuple.types); | ||
| 8283 | mem.copy(Value, values[tuple_len * i ..], operand_tuple.values); | ||
| 8284 | } | ||
| 8285 | break :rs runtime_src; | ||
| 8286 | }; | ||
| 8287 | |||
| 8288 | const tuple_ty = try Type.Tag.tuple.create(sema.arena, .{ | ||
| 8289 | .types = types, | ||
| 8290 | .values = values, | ||
| 8291 | }); | ||
| 8292 | |||
| 8293 | const runtime_src = opt_runtime_src orelse { | ||
| 8294 | const tuple_val = try Value.Tag.@"struct".create(sema.arena, values); | ||
| 8295 | return sema.addConstant(tuple_ty, tuple_val); | ||
| 8296 | }; | ||
| 8297 | |||
| 8298 | try sema.requireRuntimeBlock(block, runtime_src); | ||
| 8299 | |||
| 8300 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); | ||
| 8301 | for (operand_tuple.types) |_, i| { | ||
| 8302 | const operand_src = lhs_src; // TODO better source location | ||
| 8303 | element_refs[i] = try sema.tupleFieldValByIndex(block, operand_src, operand, @intCast(u32, i), operand_ty); | ||
| 8304 | } | ||
| 8305 | var i: usize = 1; | ||
| 8306 | while (i < factor) : (i += 1) { | ||
| 8307 | mem.copy(Air.Inst.Ref, element_refs[tuple_len * i ..], element_refs[0..tuple_len]); | ||
| 8308 | } | ||
| 8309 | |||
| 8310 | return block.addAggregateInit(tuple_ty, element_refs); | ||
| 8311 | } | ||
| 8312 | |||
| 8158 | fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8313 | fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8159 | const tracy = trace(@src()); | 8314 | const tracy = trace(@src()); |
| 8160 | defer tracy.end(); | 8315 | defer tracy.end(); |
| ... | @@ -8169,7 +8324,12 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8169,7 +8324,12 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8169 | 8324 | ||
| 8170 | // In `**` rhs has to be comptime-known, but lhs can be runtime-known | 8325 | // In `**` rhs has to be comptime-known, but lhs can be runtime-known |
| 8171 | const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); | 8326 | const factor = try sema.resolveInt(block, rhs_src, extra.rhs, Type.usize); |
| 8172 | const mulinfo = getArrayCatInfo(lhs_ty) orelse | 8327 | |
| 8328 | if (lhs_ty.isTuple()) { | ||
| 8329 | return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor); | ||
| 8330 | } | ||
| 8331 | |||
| 8332 | const mulinfo = (try sema.getArrayCatInfo(block, lhs_src, lhs)) orelse | ||
| 8173 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); | 8333 | return sema.fail(block, lhs_src, "expected array, found '{}'", .{lhs_ty}); |
| 8174 | 8334 | ||
| 8175 | const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch | 8335 | const final_len_u64 = std.math.mul(u64, mulinfo.len, factor) catch |
| ... | @@ -8180,7 +8340,8 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8180,7 +8340,8 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8180 | const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null); | 8340 | const final_len_including_sent = final_len + @boolToInt(mulinfo.sentinel != null); |
| 8181 | const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len); | 8341 | const lhs_len = try sema.usizeCast(block, lhs_src, mulinfo.len); |
| 8182 | 8342 | ||
| 8183 | const lhs_sub_val = if (lhs_ty.zigTypeTag() == .Pointer) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; | 8343 | const is_single_ptr = lhs_ty.zigTypeTag() == .Pointer and !lhs_ty.isSlice(); |
| 8344 | const lhs_sub_val = if (is_single_ptr) (try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty)).? else lhs_val; | ||
| 8184 | 8345 | ||
| 8185 | var anon_decl = try block.startAnonDecl(src); | 8346 | var anon_decl = try block.startAnonDecl(src); |
| 8186 | defer anon_decl.deinit(); | 8347 | defer anon_decl.deinit(); |
| ... | @@ -8220,7 +8381,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -8220,7 +8381,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 8220 | break :blk try Value.Tag.array.create(anon_decl.arena(), buf); | 8381 | break :blk try Value.Tag.array.create(anon_decl.arena(), buf); |
| 8221 | }; | 8382 | }; |
| 8222 | const decl = try anon_decl.finish(final_ty, val); | 8383 | const decl = try anon_decl.finish(final_ty, val); |
| 8223 | if (lhs_ty.zigTypeTag() == .Pointer) { | 8384 | if (is_single_ptr) { |
| 8224 | return sema.analyzeDeclRef(decl); | 8385 | return sema.analyzeDeclRef(decl); |
| 8225 | } else { | 8386 | } else { |
| 8226 | return sema.analyzeDeclVal(block, .unneeded, decl); | 8387 | return sema.analyzeDeclVal(block, .unneeded, decl); |
| ... | @@ -9832,14 +9993,21 @@ fn zirRetAddr( | ... | @@ -9832,14 +9993,21 @@ fn zirRetAddr( |
| 9832 | block: *Block, | 9993 | block: *Block, |
| 9833 | extended: Zir.Inst.Extended.InstData, | 9994 | extended: Zir.Inst.Extended.InstData, |
| 9834 | ) CompileError!Air.Inst.Ref { | 9995 | ) CompileError!Air.Inst.Ref { |
| 9835 | const tracy = trace(@src()); | ||
| 9836 | defer tracy.end(); | ||
| 9837 | |||
| 9838 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | 9996 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; |
| 9839 | try sema.requireRuntimeBlock(block, src); | 9997 | try sema.requireRuntimeBlock(block, src); |
| 9840 | return try block.addNoOp(.ret_addr); | 9998 | return try block.addNoOp(.ret_addr); |
| 9841 | } | 9999 | } |
| 9842 | 10000 | ||
| 10001 | fn zirFrameAddress( | ||
| 10002 | sema: *Sema, | ||
| 10003 | block: *Block, | ||
| 10004 | extended: Zir.Inst.Extended.InstData, | ||
| 10005 | ) CompileError!Air.Inst.Ref { | ||
| 10006 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | ||
| 10007 | try sema.requireRuntimeBlock(block, src); | ||
| 10008 | return try block.addNoOp(.frame_addr); | ||
| 10009 | } | ||
| 10010 | |||
| 9843 | fn zirBuiltinSrc( | 10011 | fn zirBuiltinSrc( |
| 9844 | sema: *Sema, | 10012 | sema: *Sema, |
| 9845 | block: *Block, | 10013 | block: *Block, |
| ... | @@ -11728,15 +11896,6 @@ fn zirFrame( | ... | @@ -11728,15 +11896,6 @@ fn zirFrame( |
| 11728 | return sema.fail(block, src, "TODO: Sema.zirFrame", .{}); | 11896 | return sema.fail(block, src, "TODO: Sema.zirFrame", .{}); |
| 11729 | } | 11897 | } |
| 11730 | 11898 | ||
| 11731 | fn zirFrameAddress( | ||
| 11732 | sema: *Sema, | ||
| 11733 | block: *Block, | ||
| 11734 | extended: Zir.Inst.Extended.InstData, | ||
| 11735 | ) CompileError!Air.Inst.Ref { | ||
| 11736 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | ||
| 11737 | return sema.fail(block, src, "TODO: Sema.zirFrameAddress", .{}); | ||
| 11738 | } | ||
| 11739 | |||
| 11740 | fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 11899 | fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 11741 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 11900 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 11742 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 11901 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| ... | @@ -14743,6 +14902,11 @@ fn tupleFieldVal( | ... | @@ -14743,6 +14902,11 @@ fn tupleFieldVal( |
| 14743 | tuple_ty, field_name, @errorName(err), | 14902 | tuple_ty, field_name, @errorName(err), |
| 14744 | }); | 14903 | }); |
| 14745 | }; | 14904 | }; |
| 14905 | if (field_index >= tuple_ty.structFieldCount()) { | ||
| 14906 | return sema.fail(block, field_name_src, "tuple {} has no such field '{s}'", .{ | ||
| 14907 | tuple_ty, field_name, | ||
| 14908 | }); | ||
| 14909 | } | ||
| 14746 | return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty); | 14910 | return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty); |
| 14747 | } | 14911 | } |
| 14748 | 14912 | ||
| ... | @@ -19093,6 +19257,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr | ... | @@ -19093,6 +19257,7 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr |
| 19093 | /// Used to convert a u64 value to a usize value, emitting a compile error if the number | 19257 | /// Used to convert a u64 value to a usize value, emitting a compile error if the number |
| 19094 | /// is too big to fit. | 19258 | /// is too big to fit. |
| 19095 | fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { | 19259 | fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { |
| 19260 | if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; | ||
| 19096 | return std.math.cast(usize, int) catch |err| switch (err) { | 19261 | return std.math.cast(usize, int) catch |err| switch (err) { |
| 19097 | error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}), | 19262 | error.Overflow => return sema.fail(block, src, "expression produces integer value {d} which is too big for this compiler implementation to handle", .{int}), |
| 19098 | }; | 19263 | }; |
src/arch/aarch64/CodeGen.zig+10-3| ... | @@ -579,7 +579,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -579,7 +579,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 579 | .block => try self.airBlock(inst), | 579 | .block => try self.airBlock(inst), |
| 580 | .br => try self.airBr(inst), | 580 | .br => try self.airBr(inst), |
| 581 | .breakpoint => try self.airBreakpoint(), | 581 | .breakpoint => try self.airBreakpoint(), |
| 582 | .ret_addr => try self.airRetAddr(), | 582 | .ret_addr => try self.airRetAddr(inst), |
| 583 | .frame_addr => try self.airFrameAddress(inst), | ||
| 583 | .fence => try self.airFence(), | 584 | .fence => try self.airFence(), |
| 584 | .call => try self.airCall(inst), | 585 | .call => try self.airCall(inst), |
| 585 | .cond_br => try self.airCondBr(inst), | 586 | .cond_br => try self.airCondBr(inst), |
| ... | @@ -2178,8 +2179,14 @@ fn airBreakpoint(self: *Self) !void { | ... | @@ -2178,8 +2179,14 @@ fn airBreakpoint(self: *Self) !void { |
| 2178 | return self.finishAirBookkeeping(); | 2179 | return self.finishAirBookkeeping(); |
| 2179 | } | 2180 | } |
| 2180 | 2181 | ||
| 2181 | fn airRetAddr(self: *Self) !void { | 2182 | fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { |
| 2182 | return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch}); | 2183 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{}); |
| 2184 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { | ||
| 2188 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{}); | ||
| 2189 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 2183 | } | 2190 | } |
| 2184 | 2191 | ||
| 2185 | fn airFence(self: *Self) !void { | 2192 | fn airFence(self: *Self) !void { |
src/arch/arm/CodeGen.zig+10-3| ... | @@ -565,7 +565,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -565,7 +565,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 565 | .block => try self.airBlock(inst), | 565 | .block => try self.airBlock(inst), |
| 566 | .br => try self.airBr(inst), | 566 | .br => try self.airBr(inst), |
| 567 | .breakpoint => try self.airBreakpoint(), | 567 | .breakpoint => try self.airBreakpoint(), |
| 568 | .ret_addr => try self.airRetAddr(), | 568 | .ret_addr => try self.airRetAddr(inst), |
| 569 | .frame_addr => try self.airFrameAddress(inst), | ||
| 569 | .fence => try self.airFence(), | 570 | .fence => try self.airFence(), |
| 570 | .call => try self.airCall(inst), | 571 | .call => try self.airCall(inst), |
| 571 | .cond_br => try self.airCondBr(inst), | 572 | .cond_br => try self.airCondBr(inst), |
| ... | @@ -2449,8 +2450,14 @@ fn airBreakpoint(self: *Self) !void { | ... | @@ -2449,8 +2450,14 @@ fn airBreakpoint(self: *Self) !void { |
| 2449 | return self.finishAirBookkeeping(); | 2450 | return self.finishAirBookkeeping(); |
| 2450 | } | 2451 | } |
| 2451 | 2452 | ||
| 2452 | fn airRetAddr(self: *Self) !void { | 2453 | fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { |
| 2453 | return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch}); | 2454 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for arm", .{}); |
| 2455 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 2456 | } | ||
| 2457 | |||
| 2458 | fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { | ||
| 2459 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for arm", .{}); | ||
| 2460 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 2454 | } | 2461 | } |
| 2455 | 2462 | ||
| 2456 | fn airFence(self: *Self) !void { | 2463 | fn airFence(self: *Self) !void { |
src/arch/riscv64/CodeGen.zig+10-3| ... | @@ -550,7 +550,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -550,7 +550,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 550 | .block => try self.airBlock(inst), | 550 | .block => try self.airBlock(inst), |
| 551 | .br => try self.airBr(inst), | 551 | .br => try self.airBr(inst), |
| 552 | .breakpoint => try self.airBreakpoint(), | 552 | .breakpoint => try self.airBreakpoint(), |
| 553 | .ret_addr => try self.airRetAddr(), | 553 | .ret_addr => try self.airRetAddr(inst), |
| 554 | .frame_addr => try self.airFrameAddress(inst), | ||
| 554 | .fence => try self.airFence(), | 555 | .fence => try self.airFence(), |
| 555 | .call => try self.airCall(inst), | 556 | .call => try self.airCall(inst), |
| 556 | .cond_br => try self.airCondBr(inst), | 557 | .cond_br => try self.airCondBr(inst), |
| ... | @@ -1438,8 +1439,14 @@ fn airBreakpoint(self: *Self) !void { | ... | @@ -1438,8 +1439,14 @@ fn airBreakpoint(self: *Self) !void { |
| 1438 | return self.finishAirBookkeeping(); | 1439 | return self.finishAirBookkeeping(); |
| 1439 | } | 1440 | } |
| 1440 | 1441 | ||
| 1441 | fn airRetAddr(self: *Self) !void { | 1442 | fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { |
| 1442 | return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch}); | 1443 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{}); |
| 1444 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { | ||
| 1448 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{}); | ||
| 1449 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 1443 | } | 1450 | } |
| 1444 | 1451 | ||
| 1445 | fn airFence(self: *Self) !void { | 1452 | fn airFence(self: *Self) !void { |
src/arch/wasm/CodeGen.zig+1| ... | @@ -1683,6 +1683,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { | ... | @@ -1683,6 +1683,7 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1683 | .assembly, | 1683 | .assembly, |
| 1684 | .shl_sat, | 1684 | .shl_sat, |
| 1685 | .ret_addr, | 1685 | .ret_addr, |
| 1686 | .frame_addr, | ||
| 1686 | .clz, | 1687 | .clz, |
| 1687 | .ctz, | 1688 | .ctz, |
| 1688 | .popcount, | 1689 | .popcount, |
src/arch/x86_64/CodeGen.zig+10-3| ... | @@ -662,7 +662,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { | ... | @@ -662,7 +662,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 662 | .block => try self.airBlock(inst), | 662 | .block => try self.airBlock(inst), |
| 663 | .br => try self.airBr(inst), | 663 | .br => try self.airBr(inst), |
| 664 | .breakpoint => try self.airBreakpoint(), | 664 | .breakpoint => try self.airBreakpoint(), |
| 665 | .ret_addr => try self.airRetAddr(), | 665 | .ret_addr => try self.airRetAddr(inst), |
| 666 | .frame_addr => try self.airFrameAddress(inst), | ||
| 666 | .fence => try self.airFence(), | 667 | .fence => try self.airFence(), |
| 667 | .call => try self.airCall(inst), | 668 | .call => try self.airCall(inst), |
| 668 | .cond_br => try self.airCondBr(inst), | 669 | .cond_br => try self.airCondBr(inst), |
| ... | @@ -3127,8 +3128,14 @@ fn airBreakpoint(self: *Self) !void { | ... | @@ -3127,8 +3128,14 @@ fn airBreakpoint(self: *Self) !void { |
| 3127 | return self.finishAirBookkeeping(); | 3128 | return self.finishAirBookkeeping(); |
| 3128 | } | 3129 | } |
| 3129 | 3130 | ||
| 3130 | fn airRetAddr(self: *Self) !void { | 3131 | fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { |
| 3131 | return self.fail("TODO implement airRetAddr for {}", .{self.target.cpu.arch}); | 3132 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for x86_64", .{}); |
| 3133 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 3134 | } | ||
| 3135 | |||
| 3136 | fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { | ||
| 3137 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for x86_64", .{}); | ||
| 3138 | return self.finishAir(inst, result, .{ .none, .none, .none }); | ||
| 3132 | } | 3139 | } |
| 3133 | 3140 | ||
| 3134 | fn airFence(self: *Self) !void { | 3141 | fn airFence(self: *Self) !void { |
src/codegen/c.zig+11-2| ... | @@ -1588,7 +1588,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO | ... | @@ -1588,7 +1588,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO |
| 1588 | .arg => airArg(f), | 1588 | .arg => airArg(f), |
| 1589 | 1589 | ||
| 1590 | .breakpoint => try airBreakpoint(f), | 1590 | .breakpoint => try airBreakpoint(f), |
| 1591 | .ret_addr => try airRetAddr(f), | 1591 | .ret_addr => try airRetAddr(f, inst), |
| 1592 | .frame_addr => try airFrameAddress(f, inst), | ||
| 1592 | .unreach => try airUnreach(f), | 1593 | .unreach => try airUnreach(f), |
| 1593 | .fence => try airFence(f, inst), | 1594 | .fence => try airFence(f, inst), |
| 1594 | 1595 | ||
| ... | @@ -2717,12 +2718,20 @@ fn airBreakpoint(f: *Function) !CValue { | ... | @@ -2717,12 +2718,20 @@ fn airBreakpoint(f: *Function) !CValue { |
| 2717 | return CValue.none; | 2718 | return CValue.none; |
| 2718 | } | 2719 | } |
| 2719 | 2720 | ||
| 2720 | fn airRetAddr(f: *Function) !CValue { | 2721 | fn airRetAddr(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2722 | if (f.liveness.isUnused(inst)) return CValue.none; | ||
| 2721 | const local = try f.allocLocal(Type.usize, .Const); | 2723 | const local = try f.allocLocal(Type.usize, .Const); |
| 2722 | try f.object.writer().writeAll(" = zig_return_address();\n"); | 2724 | try f.object.writer().writeAll(" = zig_return_address();\n"); |
| 2723 | return local; | 2725 | return local; |
| 2724 | } | 2726 | } |
| 2725 | 2727 | ||
| 2728 | fn airFrameAddress(f: *Function, inst: Air.Inst.Index) !CValue { | ||
| 2729 | if (f.liveness.isUnused(inst)) return CValue.none; | ||
| 2730 | const local = try f.allocLocal(Type.usize, .Const); | ||
| 2731 | try f.object.writer().writeAll(" = zig_frame_address();\n"); | ||
| 2732 | return local; | ||
| 2733 | } | ||
| 2734 | |||
| 2726 | fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { | 2735 | fn airFence(f: *Function, inst: Air.Inst.Index) !CValue { |
| 2727 | const atomic_order = f.air.instructions.items(.data)[inst].fence; | 2736 | const atomic_order = f.air.instructions.items(.data)[inst].fence; |
| 2728 | const writer = f.object.writer(); | 2737 | const writer = f.object.writer(); |
src/codegen/llvm.zig+19-5| ... | @@ -2135,6 +2135,7 @@ pub const FuncGen = struct { | ... | @@ -2135,6 +2135,7 @@ pub const FuncGen = struct { |
| 2135 | .switch_br => try self.airSwitchBr(inst), | 2135 | .switch_br => try self.airSwitchBr(inst), |
| 2136 | .breakpoint => try self.airBreakpoint(inst), | 2136 | .breakpoint => try self.airBreakpoint(inst), |
| 2137 | .ret_addr => try self.airRetAddr(inst), | 2137 | .ret_addr => try self.airRetAddr(inst), |
| 2138 | .frame_addr => try self.airFrameAddress(inst), | ||
| 2138 | .call => try self.airCall(inst), | 2139 | .call => try self.airCall(inst), |
| 2139 | .cond_br => try self.airCondBr(inst), | 2140 | .cond_br => try self.airCondBr(inst), |
| 2140 | .intcast => try self.airIntCast(inst), | 2141 | .intcast => try self.airIntCast(inst), |
| ... | @@ -4100,12 +4101,25 @@ pub const FuncGen = struct { | ... | @@ -4100,12 +4101,25 @@ pub const FuncGen = struct { |
| 4100 | } | 4101 | } |
| 4101 | 4102 | ||
| 4102 | fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 4103 | fn airRetAddr(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
| 4103 | _ = inst; | 4104 | if (self.liveness.isUnused(inst)) return null; |
| 4104 | const i32_zero = self.context.intType(32).constNull(); | 4105 | |
| 4105 | const usize_llvm_ty = try self.dg.llvmType(Type.usize); | 4106 | const llvm_i32 = self.context.intType(32); |
| 4106 | const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{}); | 4107 | const llvm_fn = self.getIntrinsic("llvm.returnaddress", &.{}); |
| 4107 | const ptr_val = self.builder.buildCall(llvm_fn, &[_]*const llvm.Value{i32_zero}, 1, .Fast, .Auto, ""); | 4108 | const params = [_]*const llvm.Value{llvm_i32.constNull()}; |
| 4108 | return self.builder.buildPtrToInt(ptr_val, usize_llvm_ty, ""); | 4109 | const ptr_val = self.builder.buildCall(llvm_fn, &params, params.len, .Fast, .Auto, ""); |
| 4110 | const llvm_usize = try self.dg.llvmType(Type.usize); | ||
| 4111 | return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); | ||
| 4112 | } | ||
| 4113 | |||
| 4114 | fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | ||
| 4115 | if (self.liveness.isUnused(inst)) return null; | ||
| 4116 | |||
| 4117 | const llvm_i32 = self.context.intType(32); | ||
| 4118 | const llvm_fn = self.getIntrinsic("llvm.frameaddress", &.{llvm_i32}); | ||
| 4119 | const params = [_]*const llvm.Value{llvm_i32.constNull()}; | ||
| 4120 | const ptr_val = self.builder.buildCall(llvm_fn, &params, params.len, .Fast, .Auto, ""); | ||
| 4121 | const llvm_usize = try self.dg.llvmType(Type.usize); | ||
| 4122 | return self.builder.buildPtrToInt(ptr_val, llvm_usize, ""); | ||
| 4109 | } | 4123 | } |
| 4110 | 4124 | ||
| 4111 | fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { | 4125 | fn airFence(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
src/print_air.zig+1| ... | @@ -171,6 +171,7 @@ const Writer = struct { | ... | @@ -171,6 +171,7 @@ const Writer = struct { |
| 171 | .breakpoint, | 171 | .breakpoint, |
| 172 | .unreach, | 172 | .unreach, |
| 173 | .ret_addr, | 173 | .ret_addr, |
| 174 | .frame_addr, | ||
| 174 | => try w.writeNoOp(s, inst), | 175 | => try w.writeNoOp(s, inst), |
| 175 | 176 | ||
| 176 | .const_ty, | 177 | .const_ty, |
src/type.zig+13-3| ... | @@ -4531,7 +4531,15 @@ pub const Type = extern union { | ... | @@ -4531,7 +4531,15 @@ pub const Type = extern union { |
| 4531 | }; | 4531 | }; |
| 4532 | 4532 | ||
| 4533 | pub fn isTuple(ty: Type) bool { | 4533 | pub fn isTuple(ty: Type) bool { |
| 4534 | return ty.tag() == .tuple; | 4534 | return ty.tag() == .tuple or ty.tag() == .empty_struct_literal; |
| 4535 | } | ||
| 4536 | |||
| 4537 | pub fn tupleFields(ty: Type) Payload.Tuple.Data { | ||
| 4538 | return switch (ty.tag()) { | ||
| 4539 | .tuple => ty.castTag(.tuple).?.data, | ||
| 4540 | .empty_struct_literal => .{ .types = &.{}, .values = &.{} }, | ||
| 4541 | else => unreachable, | ||
| 4542 | }; | ||
| 4535 | } | 4543 | } |
| 4536 | 4544 | ||
| 4537 | /// The sub-types are named after what fields they contain. | 4545 | /// The sub-types are named after what fields they contain. |
| ... | @@ -4683,11 +4691,13 @@ pub const Type = extern union { | ... | @@ -4683,11 +4691,13 @@ pub const Type = extern union { |
| 4683 | 4691 | ||
| 4684 | pub const Tuple = struct { | 4692 | pub const Tuple = struct { |
| 4685 | base: Payload = .{ .tag = .tuple }, | 4693 | base: Payload = .{ .tag = .tuple }, |
| 4686 | data: struct { | 4694 | data: Data, |
| 4695 | |||
| 4696 | pub const Data = struct { | ||
| 4687 | types: []Type, | 4697 | types: []Type, |
| 4688 | /// unreachable_value elements are used to indicate runtime-known. | 4698 | /// unreachable_value elements are used to indicate runtime-known. |
| 4689 | values: []Value, | 4699 | values: []Value, |
| 4690 | }, | 4700 | }; |
| 4691 | }; | 4701 | }; |
| 4692 | 4702 | ||
| 4693 | pub const Union = struct { | 4703 | pub const Union = struct { |
src/value.zig+21-7| ... | @@ -1829,7 +1829,7 @@ pub const Value = extern union { | ... | @@ -1829,7 +1829,7 @@ pub const Value = extern union { |
| 1829 | assert(a_tag != .undef); | 1829 | assert(a_tag != .undef); |
| 1830 | assert(b_tag != .undef); | 1830 | assert(b_tag != .undef); |
| 1831 | if (a_tag == b_tag) switch (a_tag) { | 1831 | if (a_tag == b_tag) switch (a_tag) { |
| 1832 | .void_value, .null_value, .the_only_possible_value => return true, | 1832 | .void_value, .null_value, .the_only_possible_value, .empty_struct_value => return true, |
| 1833 | .enum_literal => { | 1833 | .enum_literal => { |
| 1834 | const a_name = a.castTag(.enum_literal).?.data; | 1834 | const a_name = a.castTag(.enum_literal).?.data; |
| 1835 | const b_name = b.castTag(.enum_literal).?.data; | 1835 | const b_name = b.castTag(.enum_literal).?.data; |
| ... | @@ -1892,10 +1892,18 @@ pub const Value = extern union { | ... | @@ -1892,10 +1892,18 @@ pub const Value = extern union { |
| 1892 | return a_payload == b_payload; | 1892 | return a_payload == b_payload; |
| 1893 | }, | 1893 | }, |
| 1894 | .@"struct" => { | 1894 | .@"struct" => { |
| 1895 | const fields = ty.structFields().values(); | ||
| 1896 | const a_field_vals = a.castTag(.@"struct").?.data; | 1895 | const a_field_vals = a.castTag(.@"struct").?.data; |
| 1897 | const b_field_vals = b.castTag(.@"struct").?.data; | 1896 | const b_field_vals = b.castTag(.@"struct").?.data; |
| 1898 | assert(a_field_vals.len == b_field_vals.len); | 1897 | assert(a_field_vals.len == b_field_vals.len); |
| 1898 | if (ty.isTuple()) { | ||
| 1899 | const types = ty.tupleFields().types; | ||
| 1900 | assert(types.len == a_field_vals.len); | ||
| 1901 | for (types) |field_ty, i| { | ||
| 1902 | if (!eql(a_field_vals[i], b_field_vals[i], field_ty)) return false; | ||
| 1903 | } | ||
| 1904 | return true; | ||
| 1905 | } | ||
| 1906 | const fields = ty.structFields().values(); | ||
| 1899 | assert(fields.len == a_field_vals.len); | 1907 | assert(fields.len == a_field_vals.len); |
| 1900 | for (fields) |field, i| { | 1908 | for (fields) |field, i| { |
| 1901 | if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false; | 1909 | if (!eql(a_field_vals[i], b_field_vals[i], field.ty)) return false; |
| ... | @@ -1967,11 +1975,10 @@ pub const Value = extern union { | ... | @@ -1967,11 +1975,10 @@ pub const Value = extern union { |
| 1967 | return true; | 1975 | return true; |
| 1968 | }, | 1976 | }, |
| 1969 | .Struct => { | 1977 | .Struct => { |
| 1970 | // must be a struct with no fields since we checked for if | 1978 | // A tuple can be represented with .empty_struct_value, |
| 1971 | // both have the struct tag above. | 1979 | // the_one_possible_value, .@"struct" in which case we could |
| 1972 | const fields = ty.structFields().values(); | 1980 | // end up here and the values are equal if the type has zero fields. |
| 1973 | assert(fields.len == 0); | 1981 | return ty.structFieldCount() != 0; |
| 1974 | return true; | ||
| 1975 | }, | 1982 | }, |
| 1976 | else => return order(a, b).compare(.eq), | 1983 | else => return order(a, b).compare(.eq), |
| 1977 | } | 1984 | } |
| ... | @@ -2024,6 +2031,13 @@ pub const Value = extern union { | ... | @@ -2024,6 +2031,13 @@ pub const Value = extern union { |
| 2024 | } | 2031 | } |
| 2025 | }, | 2032 | }, |
| 2026 | .Struct => { | 2033 | .Struct => { |
| 2034 | if (ty.isTuple()) { | ||
| 2035 | const fields = ty.tupleFields(); | ||
| 2036 | for (fields.values) |field_val, i| { | ||
| 2037 | field_val.hash(fields.types[i], hasher); | ||
| 2038 | } | ||
| 2039 | return; | ||
| 2040 | } | ||
| 2027 | const fields = ty.structFields().values(); | 2041 | const fields = ty.structFields().values(); |
| 2028 | if (fields.len == 0) return; | 2042 | if (fields.len == 0) return; |
| 2029 | const field_values = val.castTag(.@"struct").?.data; | 2043 | const field_values = val.castTag(.@"struct").?.data; |
test/behavior/tuple.zig+9-9| ... | @@ -23,28 +23,30 @@ test "tuple concatenation" { | ... | @@ -23,28 +23,30 @@ test "tuple concatenation" { |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | test "tuple multiplication" { | 25 | test "tuple multiplication" { |
| 26 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 27 | |||
| 28 | const S = struct { | 26 | const S = struct { |
| 29 | fn doTheTest() !void { | 27 | fn doTheTest() !void { |
| 30 | { | 28 | { |
| 31 | const t = .{} ** 4; | 29 | const t = .{} ** 4; |
| 32 | try expectEqual(0, @typeInfo(@TypeOf(t)).Struct.fields.len); | 30 | try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 0); |
| 33 | } | 31 | } |
| 34 | { | 32 | { |
| 35 | const t = .{'a'} ** 4; | 33 | const t = .{'a'} ** 4; |
| 36 | try expectEqual(4, @typeInfo(@TypeOf(t)).Struct.fields.len); | 34 | try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 4); |
| 37 | inline for (t) |x| try expectEqual('a', x); | 35 | inline for (t) |x| try expect(x == 'a'); |
| 38 | } | 36 | } |
| 39 | { | 37 | { |
| 40 | const t = .{ 1, 2, 3 } ** 4; | 38 | const t = .{ 1, 2, 3 } ** 4; |
| 41 | try expectEqual(12, @typeInfo(@TypeOf(t)).Struct.fields.len); | 39 | try expect(@typeInfo(@TypeOf(t)).Struct.fields.len == 12); |
| 42 | inline for (t) |x, i| try expectEqual(1 + i % 3, x); | 40 | inline for (t) |x, i| try expect(x == 1 + i % 3); |
| 43 | } | 41 | } |
| 44 | } | 42 | } |
| 45 | }; | 43 | }; |
| 46 | try S.doTheTest(); | 44 | try S.doTheTest(); |
| 47 | comptime try S.doTheTest(); | 45 | comptime try S.doTheTest(); |
| 46 | } | ||
| 47 | |||
| 48 | test "tuple concatenation" { | ||
| 49 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 48 | 50 | ||
| 49 | const T = struct { | 51 | const T = struct { |
| 50 | fn consume_tuple(tuple: anytype, len: usize) !void { | 52 | fn consume_tuple(tuple: anytype, len: usize) !void { |
| ... | @@ -86,8 +88,6 @@ test "tuple multiplication" { | ... | @@ -86,8 +88,6 @@ test "tuple multiplication" { |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | test "pass tuple to comptime var parameter" { | 90 | test "pass tuple to comptime var parameter" { |
| 89 | if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO | ||
| 90 | |||
| 91 | const S = struct { | 91 | const S = struct { |
| 92 | fn Foo(comptime args: anytype) !void { | 92 | fn Foo(comptime args: anytype) !void { |
| 93 | try expect(args[0] == 1); | 93 | try expect(args[0] == 1); |