stage2: implement @memset and @memcpy builtins

This commit is contained in:
Andrew Kelley 2021-09-24 17:33:06 -07:00
parent 87fd502fb6
commit 42aa1ea115
14 changed files with 412 additions and 36 deletions

View File

@ -321,6 +321,19 @@ pub const Inst = struct {
/// Uses the `ty_op` field.
int_to_float,
/// Given dest ptr, value, and len, set all elements at dest to value.
/// Result type is always void.
/// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
/// value, `rhs` is the length.
/// The element type may be any type, not just u8.
memset,
/// Given dest ptr, src ptr, and len, copy len elements from src to dest.
/// Result type is always void.
/// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
/// src ptr, `rhs` is the length.
/// The element type may be any type, not just u8.
memcpy,
/// Uses the `ty_pl` field with payload `Cmpxchg`.
cmpxchg_weak,
/// Uses the `ty_pl` field with payload `Cmpxchg`.
@ -628,6 +641,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.atomic_store_monotonic,
.atomic_store_release,
.atomic_store_seq_cst,
.memset,
.memcpy,
=> return Type.initTag(.void),
.ptrtoint,

View File

@ -2149,8 +2149,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.field_ptr_type,
.field_parent_ptr,
.maximum,
.memcpy,
.memset,
.minimum,
.builtin_async_call,
.c_import,
@ -2204,6 +2202,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.set_float_mode,
.set_runtime_safety,
.closure_capture,
.memcpy,
.memset,
=> break :b true,
}
} else switch (maybe_unused_result) {
@ -7576,17 +7576,17 @@ fn builtinCall(
},
.memcpy => {
const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
.dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
.source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]),
.byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
.dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
.source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
.byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
});
return rvalue(gz, rl, result, node);
},
.memset => {
const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
.dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
.byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]),
.byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
.dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
.byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
.byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
});
return rvalue(gz, rl, result, node);
},

View File

@ -361,6 +361,11 @@ fn analyzeInst(
const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
},
.memset, .memcpy => {
const pl_op = inst_datas[inst].pl_op;
const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
},
.br => {
const br = inst_datas[inst].br;
return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });

View File

@ -341,8 +341,6 @@ pub fn analyzeBody(
.field_ptr_type => try sema.zirFieldPtrType(block, inst),
.field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
.maximum => try sema.zirMaximum(block, inst),
.memcpy => try sema.zirMemcpy(block, inst),
.memset => try sema.zirMemset(block, inst),
.minimum => try sema.zirMinimum(block, inst),
.builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
.@"resume" => try sema.zirResume(block, inst),
@ -526,6 +524,16 @@ pub fn analyzeBody(
i += 1;
continue;
},
.memcpy => {
try sema.zirMemcpy(block, inst);
i += 1;
continue;
},
.memset => {
try sema.zirMemset(block, inst);
i += 1;
continue;
},
// Special case instructions to handle comptime control flow.
.@"break" => {
@ -8422,16 +8430,119 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
}
fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
const src = inst_data.src();
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{});
const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
const dest_ptr = sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
if (dest_ptr_ty.zigTypeTag() != .Pointer) {
return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
}
if (dest_ptr_ty.isConstPtr()) {
return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
}
const uncasted_src_ptr = sema.resolveInst(extra.source);
const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) {
return sema.mod.fail(&block.base, src_src, "expected pointer, found '{}'", .{
uncasted_src_ptr_ty,
});
}
const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
const wanted_src_ptr_ty = try Module.ptrType(
sema.arena,
dest_ptr_ty.elemType2(),
null,
src_ptr_info.@"align",
src_ptr_info.@"addrspace",
0,
0,
false,
src_ptr_info.@"allowzero",
src_ptr_info.@"volatile",
.Many,
);
const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src);
const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src);
const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr);
const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
const runtime_src = if (maybe_dest_ptr_val) |dest_ptr_val| rs: {
if (maybe_src_ptr_val) |src_ptr_val| {
if (maybe_len_val) |len_val| {
_ = dest_ptr_val;
_ = src_ptr_val;
_ = len_val;
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy at comptime", .{});
} else break :rs len_src;
} else break :rs src_src;
} else dest_src;
try sema.requireRuntimeBlock(block, runtime_src);
_ = try block.addInst(.{
.tag = .memcpy,
.data = .{ .pl_op = .{
.operand = dest_ptr,
.payload = try sema.addExtra(Air.Bin{
.lhs = src_ptr,
.rhs = len,
}),
} },
});
}
fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
const src = inst_data.src();
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
const dest_ptr = sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
if (dest_ptr_ty.zigTypeTag() != .Pointer) {
return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
}
if (dest_ptr_ty.isConstPtr()) {
return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
}
const elem_ty = dest_ptr_ty.elemType2();
const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src);
const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src);
const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
const runtime_src = if (maybe_dest_ptr_val) |ptr_val| rs: {
if (maybe_len_val) |len_val| {
if (try sema.resolveMaybeUndefVal(block, value_src, value)) |val| {
_ = ptr_val;
_ = len_val;
_ = val;
return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset at comptime", .{});
} else break :rs value_src;
} else break :rs len_src;
} else dest_src;
try sema.requireRuntimeBlock(block, runtime_src);
_ = try block.addInst(.{
.tag = .memset,
.data = .{ .pl_op = .{
.operand = dest_ptr,
.payload = try sema.addExtra(Air.Bin{
.lhs = value,
.rhs = len,
}),
} },
});
}
fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@ -10090,7 +10201,8 @@ fn coerceArrayPtrToMany(
// The comptime Value representation is compatible with both types.
return sema.addConstant(dest_type, val);
}
return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
try sema.requireRuntimeBlock(block, inst_src);
return sema.bitcast(block, dest_type, inst, inst_src);
}
fn analyzeDeclVal(

View File

@ -887,6 +887,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.cmpxchg_weak => try self.airCmpxchg(inst),
.atomic_rmw => try self.airAtomicRmw(inst),
.atomic_load => try self.airAtomicLoad(inst),
.memcpy => try self.airMemcpy(inst),
.memset => try self.airMemset(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@ -4883,6 +4885,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
}
fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
}
fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
}
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);

View File

@ -953,6 +953,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
.atomic_rmw => try airAtomicRmw(f, inst),
.atomic_load => try airAtomicLoad(f, inst),
.memset => try airMemset(f, inst),
.memcpy => try airMemcpy(f, inst),
.int_to_float,
.float_to_int,
@ -2005,8 +2007,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
const atomic_load = f.air.instructions.items(.data)[inst].atomic_load;
const inst_ty = f.air.typeOfIndex(inst);
const ptr = try f.resolveInst(atomic_load.ptr);
const ptr_ty = f.air.typeOf(atomic_load.ptr);
if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst))
return CValue.none;
const inst_ty = f.air.typeOfIndex(inst);
const local = try f.allocLocal(inst_ty, .Const);
const writer = f.object.writer();
@ -2036,6 +2042,44 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
return local;
}
fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
const dest_ptr = try f.resolveInst(pl_op.operand);
const value = try f.resolveInst(extra.lhs);
const len = try f.resolveInst(extra.rhs);
const writer = f.object.writer();
try writer.writeAll("memset(");
try f.writeCValue(writer, dest_ptr);
try writer.writeAll(", ");
try f.writeCValue(writer, value);
try writer.writeAll(", ");
try f.writeCValue(writer, len);
try writer.writeAll(");\n");
return CValue.none;
}
fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
const pl_op = f.air.instructions.items(.data)[inst].pl_op;
const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
const dest_ptr = try f.resolveInst(pl_op.operand);
const src_ptr = try f.resolveInst(extra.lhs);
const len = try f.resolveInst(extra.rhs);
const writer = f.object.writer();
try writer.writeAll("memcpy(");
try f.writeCValue(writer, dest_ptr);
try writer.writeAll(", ");
try f.writeCValue(writer, src_ptr);
try writer.writeAll(", ");
try f.writeCValue(writer, len);
try writer.writeAll(");\n");
return CValue.none;
}
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
return switch (order) {
.Unordered => "memory_order_relaxed",

View File

@ -1279,6 +1279,8 @@ pub const FuncGen = struct {
.fence => try self.airFence(inst),
.atomic_rmw => try self.airAtomicRmw(inst),
.atomic_load => try self.airAtomicLoad(inst),
.memset => try self.airMemset(inst),
.memcpy => try self.airMemcpy(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@ -2426,6 +2428,8 @@ pub const FuncGen = struct {
const atomic_load = self.air.instructions.items(.data)[inst].atomic_load;
const ptr = try self.resolveInst(atomic_load.ptr);
const ptr_ty = self.air.typeOf(atomic_load.ptr);
if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst))
return null;
const ordering = toLlvmAtomicOrdering(atomic_load.order);
const operand_ty = ptr_ty.elemType();
const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false);
@ -2468,6 +2472,55 @@ pub const FuncGen = struct {
return null;
}
fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const dest_ptr = try self.resolveInst(pl_op.operand);
const ptr_ty = self.air.typeOf(pl_op.operand);
const value = try self.resolveInst(extra.lhs);
const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;
const len = try self.resolveInst(extra.rhs);
const u8_llvm_ty = self.context.intType(8);
const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value;
const target = self.dg.module.getTarget();
const dest_ptr_align = ptr_ty.ptrAlignment(target);
const memset = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align);
memset.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr()));
if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
// TODO generate valgrind client request to mark byte range as undefined
// see gen_valgrind_undef() in codegen.cpp
}
return null;
}
fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
const dest_ptr = try self.resolveInst(pl_op.operand);
const dest_ptr_ty = self.air.typeOf(pl_op.operand);
const src_ptr = try self.resolveInst(extra.lhs);
const src_ptr_ty = self.air.typeOf(extra.lhs);
const len = try self.resolveInst(extra.rhs);
const u8_llvm_ty = self.context.intType(8);
const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, "");
const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
const target = self.dg.module.getTarget();
const memcpy = self.builder.buildMemCpy(
dest_ptr_u8,
dest_ptr_ty.ptrAlignment(target),
src_ptr_u8,
src_ptr_ty.ptrAlignment(target),
len,
);
memcpy.setVolatile(llvm.Bool.fromBool(is_volatile));
return null;
}
fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
const id = llvm.lookupIntrinsicID(name.ptr, name.len);
assert(id != 0);

View File

@ -632,6 +632,25 @@ pub const Builder = opaque {
DestTy: *const Type,
Name: [*:0]const u8,
) *const Value;
pub const buildMemSet = LLVMBuildMemSet;
extern fn LLVMBuildMemSet(
B: *const Builder,
Ptr: *const Value,
Val: *const Value,
Len: *const Value,
Align: c_uint,
) *const Value;
pub const buildMemCpy = LLVMBuildMemCpy;
extern fn LLVMBuildMemCpy(
B: *const Builder,
Dst: *const Value,
DstAlign: c_uint,
Src: *const Value,
SrcAlign: c_uint,
Size: *const Value,
) *const Value;
};
pub const IntPredicate = enum(c_uint) {

View File

@ -126,6 +126,7 @@
#define int128_t __int128
#define uint128_t unsigned __int128
ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
ZIG_EXTERN_C void *memset (void *, int, size_t);
static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
uint8_t thresh = max - rhs;

View File

@ -202,6 +202,8 @@ const Writer = struct {
.atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
.atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),
.atomic_rmw => try w.writeAtomicRmw(s, inst),
.memcpy => try w.writeMemcpy(s, inst),
.memset => try w.writeMemset(s, inst),
}
}
@ -322,6 +324,28 @@ const Writer = struct {
try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
}
fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
try w.writeOperand(s, inst, 0, pl_op.operand);
try s.writeAll(", ");
try w.writeOperand(s, inst, 1, extra.lhs);
try s.writeAll(", ");
try w.writeOperand(s, inst, 2, extra.rhs);
}
fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const pl_op = w.air.instructions.items(.data)[inst].pl_op;
const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
try w.writeOperand(s, inst, 0, pl_op.operand);
try s.writeAll(", ");
try w.writeOperand(s, inst, 1, extra.lhs);
try s.writeAll(", ");
try w.writeOperand(s, inst, 2, extra.rhs);
}
fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const val = w.air.values[ty_pl.payload];

View File

@ -210,8 +210,6 @@ const Writer = struct {
.mul_add,
.builtin_call,
.field_parent_ptr,
.memcpy,
.memset,
.builtin_async_call,
=> try self.writePlNode(stream, inst),
@ -222,6 +220,8 @@ const Writer = struct {
.cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst),
.atomic_store => try self.writeAtomicStore(stream, inst),
.atomic_rmw => try self.writeAtomicRmw(stream, inst),
.memcpy => try self.writeMemcpy(stream, inst),
.memset => try self.writeMemset(stream, inst),
.struct_init_anon,
.struct_init_anon_ref,
@ -692,6 +692,32 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
fn writeMemcpy(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.Memcpy, inst_data.payload_index).data;
try self.writeInstRef(stream, extra.dest);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.source);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.byte_count);
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
}
fn writeMemset(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.Memset, inst_data.payload_index).data;
try self.writeInstRef(stream, extra.dest);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.byte);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.byte_count);
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
}
fn writeStructInitAnon(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.StructInitAnon, inst_data.payload_index);

View File

@ -2391,12 +2391,11 @@ pub const Type = extern union {
};
}
/// Asserts the type is a pointer or array type.
pub fn elemType(self: Type) Type {
return switch (self.tag()) {
.vector => self.castTag(.vector).?.data.elem_type,
.array => self.castTag(.array).?.data.elem_type,
.array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
pub fn childType(ty: Type) Type {
return switch (ty.tag()) {
.vector => ty.castTag(.vector).?.data.elem_type,
.array => ty.castTag(.array).?.data.elem_type,
.array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type,
.single_const_pointer,
.single_mut_pointer,
.many_const_pointer,
@ -2405,7 +2404,7 @@ pub const Type = extern union {
.c_mut_pointer,
.const_slice,
.mut_slice,
=> self.castPointer().?.data,
=> ty.castPointer().?.data,
.array_u8,
.array_u8_sentinel_0,
@ -2415,12 +2414,70 @@ pub const Type = extern union {
=> Type.initTag(.u8),
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
.pointer => self.castTag(.pointer).?.data.pointee_type,
.pointer => ty.castTag(.pointer).?.data.pointee_type,
else => unreachable,
};
}
/// Asserts the type is a pointer or array type.
/// TODO this is deprecated in favor of `childType`.
pub const elemType = childType;
/// For *[N]T, returns T.
/// For ?*T, returns T.
/// For ?*[N]T, returns T.
/// For ?[*]T, returns T.
/// For *T, returns T.
/// For [*]T, returns T.
pub fn elemType2(ty: Type) Type {
return switch (ty.tag()) {
.vector => ty.castTag(.vector).?.data.elem_type,
.array => ty.castTag(.array).?.data.elem_type,
.array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type,
.many_const_pointer,
.many_mut_pointer,
.c_const_pointer,
.c_mut_pointer,
.const_slice,
.mut_slice,
=> ty.castPointer().?.data,
.single_const_pointer,
.single_mut_pointer,
=> ty.castPointer().?.data.shallowElemType(),
.array_u8,
.array_u8_sentinel_0,
.const_slice_u8,
.manyptr_u8,
.manyptr_const_u8,
=> Type.initTag(.u8),
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
.pointer => {
const info = ty.castTag(.pointer).?.data;
const child_ty = info.pointee_type;
if (info.size == .One) {
return child_ty.shallowElemType();
} else {
return child_ty;
}
},
// TODO handle optionals
else => unreachable,
};
}
fn shallowElemType(child_ty: Type) Type {
return switch (child_ty.zigTypeTag()) {
.Array, .Vector => child_ty.childType(),
else => child_ty,
};
}
/// Asserts that the type is an optional.
/// Resulting `Type` will have inner memory referencing `buf`.
pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {

View File

@ -170,3 +170,21 @@ test "string concatenation" {
test "array mult operator" {
try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
}
test "memcpy and memset intrinsics" {
try testMemcpyMemset();
// TODO add comptime test coverage
//comptime try testMemcpyMemset();
}
fn testMemcpyMemset() !void {
var foo: [20]u8 = undefined;
var bar: [20]u8 = undefined;
@memset(&foo, 'A', foo.len);
@memcpy(&bar, &foo, bar.len);
try expect(bar[0] == 'A');
try expect(bar[11] == 'A');
try expect(bar[19] == 'A');
}

View File

@ -5,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
const mem = std.mem;
const builtin = @import("builtin");
test "memcpy and memset intrinsics" {
var foo: [20]u8 = undefined;
var bar: [20]u8 = undefined;
@memset(&foo, 'A', foo.len);
@memcpy(&bar, &foo, bar.len);
if (bar[11] != 'A') unreachable;
}
test "slicing" {
var array: [20]i32 = undefined;