mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
stage2: implement array literal with explicit type
New ZIR instruction: elem_ptr_imm This saves some memory for array literals since the element indexes are communicated as immediate values rather than as references to other ZIR instructions.
This commit is contained in:
parent
601ac82041
commit
76335bc7ba
@ -1352,15 +1352,14 @@ fn arrayInitExprRlPtr(
|
||||
defer gpa.free(elem_ptr_list);
|
||||
|
||||
for (elements) |elem_init, i| {
|
||||
const index_inst = try gz.addInt(i);
|
||||
const elem_ptr = try gz.addPlNode(.elem_ptr_node, elem_init, Zir.Inst.Bin{
|
||||
.lhs = result_ptr,
|
||||
.rhs = index_inst,
|
||||
const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
|
||||
.ptr = result_ptr,
|
||||
.index = @intCast(u32, i),
|
||||
});
|
||||
elem_ptr_list[i] = refToIndex(elem_ptr).?;
|
||||
_ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
|
||||
}
|
||||
_ = try gz.addPlNode(.validate_array_init_ptr, node, Zir.Inst.Block{
|
||||
_ = try gz.addPlNode(.validate_array_init, node, Zir.Inst.Block{
|
||||
.body_len = @intCast(u32, elem_ptr_list.len),
|
||||
});
|
||||
try astgen.extra.appendSlice(gpa, elem_ptr_list);
|
||||
@ -1539,7 +1538,7 @@ fn structInitExprRlPtrInner(
|
||||
field_ptr_list[i] = refToIndex(field_ptr).?;
|
||||
_ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
|
||||
}
|
||||
_ = try gz.addPlNode(.validate_struct_init_ptr, node, Zir.Inst.Block{
|
||||
_ = try gz.addPlNode(.validate_struct_init, node, Zir.Inst.Block{
|
||||
.body_len = @intCast(u32, field_ptr_list.len),
|
||||
});
|
||||
try astgen.extra.appendSlice(gpa, field_ptr_list);
|
||||
@ -2040,6 +2039,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.elem_ptr,
|
||||
.elem_val,
|
||||
.elem_ptr_node,
|
||||
.elem_ptr_imm,
|
||||
.elem_val_node,
|
||||
.field_ptr,
|
||||
.field_val,
|
||||
@ -2246,8 +2246,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.store_to_block_ptr,
|
||||
.store_to_inferred_ptr,
|
||||
.resolve_inferred_alloc,
|
||||
.validate_struct_init_ptr,
|
||||
.validate_array_init_ptr,
|
||||
.validate_struct_init,
|
||||
.validate_array_init,
|
||||
.set_align_stack,
|
||||
.set_cold,
|
||||
.set_float_mode,
|
||||
|
||||
53
src/Sema.zig
53
src/Sema.zig
@ -479,6 +479,7 @@ pub fn analyzeBody(
|
||||
.load => try sema.zirLoad(block, inst),
|
||||
.elem_ptr => try sema.zirElemPtr(block, inst),
|
||||
.elem_ptr_node => try sema.zirElemPtrNode(block, inst),
|
||||
.elem_ptr_imm => try sema.zirElemPtrImm(block, inst),
|
||||
.elem_val => try sema.zirElemVal(block, inst),
|
||||
.elem_val_node => try sema.zirElemValNode(block, inst),
|
||||
.elem_type => try sema.zirElemType(block, inst),
|
||||
@ -741,13 +742,13 @@ pub fn analyzeBody(
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.validate_struct_init_ptr => {
|
||||
try sema.zirValidateStructInitPtr(block, inst);
|
||||
.validate_struct_init => {
|
||||
try sema.zirValidateStructInit(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.validate_array_init_ptr => {
|
||||
try sema.zirValidateArrayInitPtr(block, inst);
|
||||
.validate_array_init => {
|
||||
try sema.zirValidateArrayInit(block, inst);
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
@ -2106,7 +2107,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
|
||||
}
|
||||
}
|
||||
|
||||
fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
fn zirValidateStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -2117,15 +2118,15 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Co
|
||||
const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
|
||||
const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
|
||||
const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
|
||||
const agg_ty = sema.typeOf(object_ptr).elemType();
|
||||
const agg_ty = sema.typeOf(object_ptr).childType();
|
||||
switch (agg_ty.zigTypeTag()) {
|
||||
.Struct => return sema.validateStructInitPtr(
|
||||
.Struct => return sema.validateStructInit(
|
||||
block,
|
||||
agg_ty.castTag(.@"struct").?.data,
|
||||
init_src,
|
||||
instrs,
|
||||
),
|
||||
.Union => return sema.validateUnionInitPtr(
|
||||
.Union => return sema.validateUnionInit(
|
||||
block,
|
||||
agg_ty.cast(Type.Payload.Union).?.data,
|
||||
init_src,
|
||||
@ -2136,7 +2137,7 @@ fn zirValidateStructInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Co
|
||||
}
|
||||
}
|
||||
|
||||
fn validateUnionInitPtr(
|
||||
fn validateUnionInit(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
union_obj: *Module.Union,
|
||||
@ -2175,7 +2176,7 @@ fn validateUnionInitPtr(
|
||||
_ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
|
||||
}
|
||||
|
||||
fn validateStructInitPtr(
|
||||
fn validateStructInit(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
struct_obj: *Module.Struct,
|
||||
@ -2239,10 +2240,22 @@ fn validateStructInitPtr(
|
||||
}
|
||||
}
|
||||
|
||||
fn zirValidateArrayInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO implement Sema.zirValidateArrayInitPtr", .{});
|
||||
fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const init_src = validate_inst.src();
|
||||
const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
|
||||
const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
|
||||
const elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
|
||||
const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, elem_ptr_data.payload_index).data;
|
||||
const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
|
||||
const array_ty = sema.typeOf(array_ptr).childType();
|
||||
const array_len = array_ty.arrayLen();
|
||||
|
||||
if (instrs.len != array_len) {
|
||||
return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
|
||||
array_len, instrs.len,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn failWithBadFieldAccess(
|
||||
@ -5169,6 +5182,18 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
|
||||
return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src);
|
||||
}
|
||||
|
||||
fn zirElemPtrImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
|
||||
const array_ptr = sema.resolveInst(extra.ptr);
|
||||
const elem_index = try sema.addIntUnsigned(Type.usize, extra.index);
|
||||
return sema.elemPtr(block, src, array_ptr, elem_index, src);
|
||||
}
|
||||
|
||||
fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
28
src/Zir.zig
28
src/Zir.zig
@ -344,6 +344,11 @@ pub const Inst = struct {
|
||||
/// Same as `elem_ptr` except also stores a source location node.
|
||||
/// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
|
||||
elem_ptr_node,
|
||||
/// Same as `elem_ptr_node` except the index is stored immediately rather than
|
||||
/// as a reference to another ZIR instruction.
|
||||
/// Uses the `pl_node` union field. AST node is an element inside array initialization
|
||||
/// syntax. Payload is `ElemPtrImm`.
|
||||
elem_ptr_imm,
|
||||
/// Given an array, slice, or pointer, returns the element at the provided index.
|
||||
/// Uses the `bin` union field. Source location is implied to be the same
|
||||
/// as the previous instruction.
|
||||
@ -675,14 +680,14 @@ pub const Inst = struct {
|
||||
/// This instruction asserts that there is at least one field_ptr instruction,
|
||||
/// because it must use one of them to find out the struct type.
|
||||
/// Uses the `pl_node` field. Payload is `Block`.
|
||||
validate_struct_init_ptr,
|
||||
/// Given a set of `elem_ptr_node` instructions, assumes they are all part of an
|
||||
validate_struct_init,
|
||||
/// Given a set of `elem_ptr_imm` instructions, assumes they are all part of an
|
||||
/// array initialization expression, and emits a compile error if the number of
|
||||
/// elements does not match the array type.
|
||||
/// This instruction asserts that there is at least one elem_ptr_node instruction,
|
||||
/// This instruction asserts that there is at least one `elem_ptr_imm` instruction,
|
||||
/// because it must use one of them to find out the array type.
|
||||
/// Uses the `pl_node` field. Payload is `Block`.
|
||||
validate_array_init_ptr,
|
||||
validate_array_init,
|
||||
/// A struct literal with a specified type, with no fields.
|
||||
/// Uses the `un_node` field.
|
||||
struct_init_empty,
|
||||
@ -1023,6 +1028,7 @@ pub const Inst = struct {
|
||||
.elem_ptr,
|
||||
.elem_val,
|
||||
.elem_ptr_node,
|
||||
.elem_ptr_imm,
|
||||
.elem_val_node,
|
||||
.ensure_result_used,
|
||||
.ensure_result_non_error,
|
||||
@ -1114,8 +1120,8 @@ pub const Inst = struct {
|
||||
.switch_block_ref_else_multi,
|
||||
.switch_block_ref_under,
|
||||
.switch_block_ref_under_multi,
|
||||
.validate_struct_init_ptr,
|
||||
.validate_array_init_ptr,
|
||||
.validate_struct_init,
|
||||
.validate_array_init,
|
||||
.struct_init_empty,
|
||||
.struct_init,
|
||||
.struct_init_ref,
|
||||
@ -1291,6 +1297,7 @@ pub const Inst = struct {
|
||||
.div = .pl_node,
|
||||
.elem_ptr = .bin,
|
||||
.elem_ptr_node = .pl_node,
|
||||
.elem_ptr_imm = .pl_node,
|
||||
.elem_val = .bin,
|
||||
.elem_val_node = .pl_node,
|
||||
.ensure_result_used = .un_node,
|
||||
@ -1377,8 +1384,8 @@ pub const Inst = struct {
|
||||
.switch_capture_multi_ref = .switch_capture,
|
||||
.switch_capture_else = .switch_capture,
|
||||
.switch_capture_else_ref = .switch_capture,
|
||||
.validate_struct_init_ptr = .pl_node,
|
||||
.validate_array_init_ptr = .pl_node,
|
||||
.validate_struct_init = .pl_node,
|
||||
.validate_array_init = .pl_node,
|
||||
.struct_init_empty = .un_node,
|
||||
.field_type = .pl_node,
|
||||
.field_type_ref = .pl_node,
|
||||
@ -2459,6 +2466,11 @@ pub const Inst = struct {
|
||||
operand: Ref,
|
||||
};
|
||||
|
||||
pub const ElemPtrImm = struct {
|
||||
ptr: Ref,
|
||||
index: u32,
|
||||
};
|
||||
|
||||
/// This form is supported when there are no ranges, and exactly 1 item per block.
|
||||
/// Depending on zir tag and len fields, extra fields trail
|
||||
/// this one in the extra array.
|
||||
|
||||
@ -355,6 +355,8 @@ const Writer = struct {
|
||||
.elem_val_node,
|
||||
=> try self.writePlNodeBin(stream, inst),
|
||||
|
||||
.elem_ptr_imm => try self.writeElemPtrImm(stream, inst),
|
||||
|
||||
.@"export" => try self.writePlNodeExport(stream, inst),
|
||||
.export_value => try self.writePlNodeExportValue(stream, inst),
|
||||
|
||||
@ -364,8 +366,8 @@ const Writer = struct {
|
||||
.block_inline,
|
||||
.suspend_block,
|
||||
.loop,
|
||||
.validate_struct_init_ptr,
|
||||
.validate_array_init_ptr,
|
||||
.validate_struct_init,
|
||||
.validate_array_init,
|
||||
.c_import,
|
||||
=> try self.writePlNodeBlock(stream, inst),
|
||||
|
||||
@ -809,6 +811,15 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeElemPtrImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
|
||||
|
||||
try self.writeInstRef(stream, extra.ptr);
|
||||
try stream.print(", {d}) ", .{extra.index});
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
|
||||
|
||||
@ -33,8 +33,13 @@ test "array init with mult" {
|
||||
var i: [8]u8 = [2]u8{ a, 'b' } ** 4;
|
||||
try expect(std.mem.eql(u8, &i, "abababab"));
|
||||
|
||||
// this should cause a Value.repeated to be emitted in AIR.
|
||||
// TODO: find a way to test that this is actually getting emmited
|
||||
var j: [4]u8 = [1]u8{'a'} ** 4;
|
||||
try expect(std.mem.eql(u8, &j, "aaaa"));
|
||||
}
|
||||
|
||||
test "array literal with explicit type" {
|
||||
const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 };
|
||||
|
||||
try expect(hex_mult.len == 4);
|
||||
try expect(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
@ -4,6 +4,13 @@ const mem = std.mem;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
|
||||
test "array literal with inferred length" {
|
||||
const hex_mult = [_]u16{ 4096, 256, 16, 1 };
|
||||
|
||||
try expect(hex_mult.len == 4);
|
||||
try expect(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array with sentinels" {
|
||||
const S = struct {
|
||||
fn doTheTest(is_ct: bool) !void {
|
||||
@ -39,13 +46,6 @@ test "void arrays" {
|
||||
try expect(array.len == 4);
|
||||
}
|
||||
|
||||
test "array literal" {
|
||||
const hex_mult = [_]u16{ 4096, 256, 16, 1 };
|
||||
|
||||
try expect(hex_mult.len == 4);
|
||||
try expect(hex_mult[1] == 256);
|
||||
}
|
||||
|
||||
test "array dot len const expr" {
|
||||
try expect(comptime x: {
|
||||
break :x some_array.len == 4;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user