authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-24 17:33:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-09-24 17:33:06-07:00
log42aa1ea115eca3dcc704eddf020ce87271a41174
tree3242b53425c599af929446e6776a2f6b3a0c6966
parent87fd502fb68f8f488e6eba6b1f7d70902d6bfe5a

stage2: implement `@memset` and `@memcpy` builtins


14 files changed, 412 insertions(+), 36 deletions(-)

src/Air.zig+15
......@@ -321,6 +321,19 @@ pub const Inst = struct {
321321 /// Uses the `ty_op` field.
322322 int_to_float,
323323
324 /// Given dest ptr, value, and len, set all elements at dest to value.
325 /// Result type is always void.
326 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
327 /// value, `rhs` is the length.
328 /// The element type may be any type, not just u8.
329 memset,
330 /// Given dest ptr, src ptr, and len, copy len elements from src to dest.
331 /// Result type is always void.
332 /// Uses the `pl_op` field. Operand is the dest ptr. Payload is `Bin`. `lhs` is the
333 /// src ptr, `rhs` is the length.
334 /// The element type may be any type, not just u8.
335 memcpy,
336
324337 /// Uses the `ty_pl` field with payload `Cmpxchg`.
325338 cmpxchg_weak,
326339 /// Uses the `ty_pl` field with payload `Cmpxchg`.
......@@ -628,6 +641,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
628641 .atomic_store_monotonic,
629642 .atomic_store_release,
630643 .atomic_store_seq_cst,
644 .memset,
645 .memcpy,
631646 => return Type.initTag(.void),
632647
633648 .ptrtoint,
src/AstGen.zig+8-8
......@@ -2149,8 +2149,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21492149 .field_ptr_type,
21502150 .field_parent_ptr,
21512151 .maximum,
2152 .memcpy,
2153 .memset,
21542152 .minimum,
21552153 .builtin_async_call,
21562154 .c_import,
......@@ -2204,6 +2202,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
22042202 .set_float_mode,
22052203 .set_runtime_safety,
22062204 .closure_capture,
2205 .memcpy,
2206 .memset,
22072207 => break :b true,
22082208 }
22092209 } else switch (maybe_unused_result) {
......@@ -7576,17 +7576,17 @@ fn builtinCall(
75767576 },
75777577 .memcpy => {
75787578 const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
7579 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
7580 .source = try expr(gz, scope, .{ .ty = .manyptr_const_u8_type }, params[1]),
7581 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
7579 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7580 .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
7581 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
75827582 });
75837583 return rvalue(gz, rl, result, node);
75847584 },
75857585 .memset => {
75867586 const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
7587 .dest = try expr(gz, scope, .{ .ty = .manyptr_u8_type }, params[0]),
7588 .byte = try expr(gz, scope, .{ .ty = .u8_type }, params[1]),
7589 .byte_count = try expr(gz, scope, .{ .ty = .usize_type }, params[2]),
7587 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
7588 .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
7589 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
75907590 });
75917591 return rvalue(gz, rl, result, node);
75927592 },
src/Liveness.zig+5
......@@ -361,6 +361,11 @@ fn analyzeInst(
361361 const extra = a.air.extraData(Air.AtomicRmw, pl_op.payload).data;
362362 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.operand, .none });
363363 },
364 .memset, .memcpy => {
365 const pl_op = inst_datas[inst].pl_op;
366 const extra = a.air.extraData(Air.Bin, pl_op.payload).data;
367 return trackOperands(a, new_set, inst, main_tomb, .{ pl_op.operand, extra.lhs, extra.rhs });
368 },
364369 .br => {
365370 const br = inst_datas[inst].br;
366371 return trackOperands(a, new_set, inst, main_tomb, .{ br.operand, .none, .none });
src/Sema.zig+119-7
......@@ -341,8 +341,6 @@ pub fn analyzeBody(
341341 .field_ptr_type => try sema.zirFieldPtrType(block, inst),
342342 .field_parent_ptr => try sema.zirFieldParentPtr(block, inst),
343343 .maximum => try sema.zirMaximum(block, inst),
344 .memcpy => try sema.zirMemcpy(block, inst),
345 .memset => try sema.zirMemset(block, inst),
346344 .minimum => try sema.zirMinimum(block, inst),
347345 .builtin_async_call => try sema.zirBuiltinAsyncCall(block, inst),
348346 .@"resume" => try sema.zirResume(block, inst),
......@@ -526,6 +524,16 @@ pub fn analyzeBody(
526524 i += 1;
527525 continue;
528526 },
527 .memcpy => {
528 try sema.zirMemcpy(block, inst);
529 i += 1;
530 continue;
531 },
532 .memset => {
533 try sema.zirMemset(block, inst);
534 i += 1;
535 continue;
536 },
529537
530538 // Special case instructions to handle comptime control flow.
531539 .@"break" => {
......@@ -8422,16 +8430,119 @@ fn zirMaximum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr
84228430 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMaximum", .{});
84238431}
84248432
8425fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8433fn zirMemcpy(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
84268434 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8435 const extra = sema.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
84278436 const src = inst_data.src();
8428 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy", .{});
8437 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8438 const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8439 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
8440 const dest_ptr = sema.resolveInst(extra.dest);
8441 const dest_ptr_ty = sema.typeOf(dest_ptr);
8442
8443 if (dest_ptr_ty.zigTypeTag() != .Pointer) {
8444 return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
8445 }
8446 if (dest_ptr_ty.isConstPtr()) {
8447 return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
8448 }
8449
8450 const uncasted_src_ptr = sema.resolveInst(extra.source);
8451 const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
8452 if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) {
8453 return sema.mod.fail(&block.base, src_src, "expected pointer, found '{}'", .{
8454 uncasted_src_ptr_ty,
8455 });
8456 }
8457 const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
8458 const wanted_src_ptr_ty = try Module.ptrType(
8459 sema.arena,
8460 dest_ptr_ty.elemType2(),
8461 null,
8462 src_ptr_info.@"align",
8463 src_ptr_info.@"addrspace",
8464 0,
8465 0,
8466 false,
8467 src_ptr_info.@"allowzero",
8468 src_ptr_info.@"volatile",
8469 .Many,
8470 );
8471 const src_ptr = try sema.coerce(block, wanted_src_ptr_ty, uncasted_src_ptr, src_src);
8472 const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src);
8473
8474 const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
8475 const maybe_src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr);
8476 const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
8477
8478 const runtime_src = if (maybe_dest_ptr_val) |dest_ptr_val| rs: {
8479 if (maybe_src_ptr_val) |src_ptr_val| {
8480 if (maybe_len_val) |len_val| {
8481 _ = dest_ptr_val;
8482 _ = src_ptr_val;
8483 _ = len_val;
8484 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemcpy at comptime", .{});
8485 } else break :rs len_src;
8486 } else break :rs src_src;
8487 } else dest_src;
8488
8489 try sema.requireRuntimeBlock(block, runtime_src);
8490 _ = try block.addInst(.{
8491 .tag = .memcpy,
8492 .data = .{ .pl_op = .{
8493 .operand = dest_ptr,
8494 .payload = try sema.addExtra(Air.Bin{
8495 .lhs = src_ptr,
8496 .rhs = len,
8497 }),
8498 } },
8499 });
84298500}
84308501
8431fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8502fn zirMemset(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!void {
84328503 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
8504 const extra = sema.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
84338505 const src = inst_data.src();
8434 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset", .{});
8506 const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
8507 const value_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
8508 const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
8509 const dest_ptr = sema.resolveInst(extra.dest);
8510 const dest_ptr_ty = sema.typeOf(dest_ptr);
8511 if (dest_ptr_ty.zigTypeTag() != .Pointer) {
8512 return sema.mod.fail(&block.base, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
8513 }
8514 if (dest_ptr_ty.isConstPtr()) {
8515 return sema.mod.fail(&block.base, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
8516 }
8517 const elem_ty = dest_ptr_ty.elemType2();
8518 const value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.byte), value_src);
8519 const len = try sema.coerce(block, Type.initTag(.usize), sema.resolveInst(extra.byte_count), len_src);
8520
8521 const maybe_dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr);
8522 const maybe_len_val = try sema.resolveDefinedValue(block, len_src, len);
8523
8524 const runtime_src = if (maybe_dest_ptr_val) |ptr_val| rs: {
8525 if (maybe_len_val) |len_val| {
8526 if (try sema.resolveMaybeUndefVal(block, value_src, value)) |val| {
8527 _ = ptr_val;
8528 _ = len_val;
8529 _ = val;
8530 return sema.mod.fail(&block.base, src, "TODO: Sema.zirMemset at comptime", .{});
8531 } else break :rs value_src;
8532 } else break :rs len_src;
8533 } else dest_src;
8534
8535 try sema.requireRuntimeBlock(block, runtime_src);
8536 _ = try block.addInst(.{
8537 .tag = .memset,
8538 .data = .{ .pl_op = .{
8539 .operand = dest_ptr,
8540 .payload = try sema.addExtra(Air.Bin{
8541 .lhs = value,
8542 .rhs = len,
8543 }),
8544 } },
8545 });
84358546}
84368547
84378548fn zirMinimum(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
......@@ -10090,7 +10201,8 @@ fn coerceArrayPtrToMany(
1009010201 // The comptime Value representation is compatible with both types.
1009110202 return sema.addConstant(dest_type, val);
1009210203 }
10093 return sema.mod.fail(&block.base, inst_src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
10204 try sema.requireRuntimeBlock(block, inst_src);
10205 return sema.bitcast(block, dest_type, inst, inst_src);
1009410206}
1009510207
1009610208fn analyzeDeclVal(
src/codegen.zig+12
......@@ -887,6 +887,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
887887 .cmpxchg_weak => try self.airCmpxchg(inst),
888888 .atomic_rmw => try self.airAtomicRmw(inst),
889889 .atomic_load => try self.airAtomicLoad(inst),
890 .memcpy => try self.airMemcpy(inst),
891 .memset => try self.airMemset(inst),
890892
891893 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
892894 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -4883,6 +4885,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
48834885 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
48844886 }
48854887
4888 fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
4889 _ = inst;
4890 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
4891 }
4892
4893 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
4894 _ = inst;
4895 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
4896 }
4897
48864898 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
48874899 // First section of indexes correspond to a set number of constant values.
48884900 const ref_int = @enumToInt(inst);
src/codegen/c.zig+45-1
......@@ -953,6 +953,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
953953 .cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
954954 .atomic_rmw => try airAtomicRmw(f, inst),
955955 .atomic_load => try airAtomicLoad(f, inst),
956 .memset => try airMemset(f, inst),
957 .memcpy => try airMemcpy(f, inst),
956958
957959 .int_to_float,
958960 .float_to_int,
......@@ -2005,8 +2007,12 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue {
20052007
20062008fn airAtomicLoad(f: *Function, inst: Air.Inst.Index) !CValue {
20072009 const atomic_load = f.air.instructions.items(.data)[inst].atomic_load;
2008 const inst_ty = f.air.typeOfIndex(inst);
20092010 const ptr = try f.resolveInst(atomic_load.ptr);
2011 const ptr_ty = f.air.typeOf(atomic_load.ptr);
2012 if (!ptr_ty.isVolatilePtr() and f.liveness.isUnused(inst))
2013 return CValue.none;
2014
2015 const inst_ty = f.air.typeOfIndex(inst);
20102016 const local = try f.allocLocal(inst_ty, .Const);
20112017 const writer = f.object.writer();
20122018
......@@ -2036,6 +2042,44 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
20362042 return local;
20372043}
20382044
2045fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
2046 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
2047 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
2048 const dest_ptr = try f.resolveInst(pl_op.operand);
2049 const value = try f.resolveInst(extra.lhs);
2050 const len = try f.resolveInst(extra.rhs);
2051 const writer = f.object.writer();
2052
2053 try writer.writeAll("memset(");
2054 try f.writeCValue(writer, dest_ptr);
2055 try writer.writeAll(", ");
2056 try f.writeCValue(writer, value);
2057 try writer.writeAll(", ");
2058 try f.writeCValue(writer, len);
2059 try writer.writeAll(");\n");
2060
2061 return CValue.none;
2062}
2063
2064fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
2065 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
2066 const extra = f.air.extraData(Air.Bin, pl_op.payload).data;
2067 const dest_ptr = try f.resolveInst(pl_op.operand);
2068 const src_ptr = try f.resolveInst(extra.lhs);
2069 const len = try f.resolveInst(extra.rhs);
2070 const writer = f.object.writer();
2071
2072 try writer.writeAll("memcpy(");
2073 try f.writeCValue(writer, dest_ptr);
2074 try writer.writeAll(", ");
2075 try f.writeCValue(writer, src_ptr);
2076 try writer.writeAll(", ");
2077 try f.writeCValue(writer, len);
2078 try writer.writeAll(");\n");
2079
2080 return CValue.none;
2081}
2082
20392083fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
20402084 return switch (order) {
20412085 .Unordered => "memory_order_relaxed",
src/codegen/llvm.zig+53
......@@ -1279,6 +1279,8 @@ pub const FuncGen = struct {
12791279 .fence => try self.airFence(inst),
12801280 .atomic_rmw => try self.airAtomicRmw(inst),
12811281 .atomic_load => try self.airAtomicLoad(inst),
1282 .memset => try self.airMemset(inst),
1283 .memcpy => try self.airMemcpy(inst),
12821284
12831285 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
12841286 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
......@@ -2426,6 +2428,8 @@ pub const FuncGen = struct {
24262428 const atomic_load = self.air.instructions.items(.data)[inst].atomic_load;
24272429 const ptr = try self.resolveInst(atomic_load.ptr);
24282430 const ptr_ty = self.air.typeOf(atomic_load.ptr);
2431 if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst))
2432 return null;
24292433 const ordering = toLlvmAtomicOrdering(atomic_load.order);
24302434 const operand_ty = ptr_ty.elemType();
24312435 const opt_abi_ty = self.dg.getAtomicAbiType(operand_ty, false);
......@@ -2468,6 +2472,55 @@ pub const FuncGen = struct {
24682472 return null;
24692473 }
24702474
2475 fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2476 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2477 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
2478 const dest_ptr = try self.resolveInst(pl_op.operand);
2479 const ptr_ty = self.air.typeOf(pl_op.operand);
2480 const value = try self.resolveInst(extra.lhs);
2481 const val_is_undef = if (self.air.value(extra.lhs)) |val| val.isUndef() else false;
2482 const len = try self.resolveInst(extra.rhs);
2483 const u8_llvm_ty = self.context.intType(8);
2484 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
2485 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
2486 const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value;
2487 const target = self.dg.module.getTarget();
2488 const dest_ptr_align = ptr_ty.ptrAlignment(target);
2489 const memset = self.builder.buildMemSet(dest_ptr_u8, fill_char, len, dest_ptr_align);
2490 memset.setVolatile(llvm.Bool.fromBool(ptr_ty.isVolatilePtr()));
2491
2492 if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
2493 // TODO generate valgrind client request to mark byte range as undefined
2494 // see gen_valgrind_undef() in codegen.cpp
2495 }
2496 return null;
2497 }
2498
2499 fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
2500 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2501 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
2502 const dest_ptr = try self.resolveInst(pl_op.operand);
2503 const dest_ptr_ty = self.air.typeOf(pl_op.operand);
2504 const src_ptr = try self.resolveInst(extra.lhs);
2505 const src_ptr_ty = self.air.typeOf(extra.lhs);
2506 const len = try self.resolveInst(extra.rhs);
2507 const u8_llvm_ty = self.context.intType(8);
2508 const ptr_u8_llvm_ty = u8_llvm_ty.pointerType(0);
2509 const dest_ptr_u8 = self.builder.buildBitCast(dest_ptr, ptr_u8_llvm_ty, "");
2510 const src_ptr_u8 = self.builder.buildBitCast(src_ptr, ptr_u8_llvm_ty, "");
2511 const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
2512 const target = self.dg.module.getTarget();
2513 const memcpy = self.builder.buildMemCpy(
2514 dest_ptr_u8,
2515 dest_ptr_ty.ptrAlignment(target),
2516 src_ptr_u8,
2517 src_ptr_ty.ptrAlignment(target),
2518 len,
2519 );
2520 memcpy.setVolatile(llvm.Bool.fromBool(is_volatile));
2521 return null;
2522 }
2523
24712524 fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
24722525 const id = llvm.lookupIntrinsicID(name.ptr, name.len);
24732526 assert(id != 0);
src/codegen/llvm/bindings.zig+19
......@@ -632,6 +632,25 @@ pub const Builder = opaque {
632632 DestTy: *const Type,
633633 Name: [*:0]const u8,
634634 ) *const Value;
635
636 pub const buildMemSet = LLVMBuildMemSet;
637 extern fn LLVMBuildMemSet(
638 B: *const Builder,
639 Ptr: *const Value,
640 Val: *const Value,
641 Len: *const Value,
642 Align: c_uint,
643 ) *const Value;
644
645 pub const buildMemCpy = LLVMBuildMemCpy;
646 extern fn LLVMBuildMemCpy(
647 B: *const Builder,
648 Dst: *const Value,
649 DstAlign: c_uint,
650 Src: *const Value,
651 SrcAlign: c_uint,
652 Size: *const Value,
653 ) *const Value;
635654};
636655
637656pub const IntPredicate = enum(c_uint) {
src/link/C/zig.h+1
......@@ -126,6 +126,7 @@
126126#define int128_t __int128
127127#define uint128_t unsigned __int128
128128ZIG_EXTERN_C void *memcpy (void *ZIG_RESTRICT, const void *ZIG_RESTRICT, size_t);
129ZIG_EXTERN_C void *memset (void *, int, size_t);
129130
130131static inline uint8_t zig_addw_u8(uint8_t lhs, uint8_t rhs, uint8_t max) {
131132 uint8_t thresh = max - rhs;
src/print_air.zig+24
......@@ -202,6 +202,8 @@ const Writer = struct {
202202 .atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
203203 .atomic_store_seq_cst => try w.writeAtomicStore(s, inst, .SeqCst),
204204 .atomic_rmw => try w.writeAtomicRmw(s, inst),
205 .memcpy => try w.writeMemcpy(s, inst),
206 .memset => try w.writeMemset(s, inst),
205207 }
206208 }
207209
......@@ -322,6 +324,28 @@ const Writer = struct {
322324 try s.print(", {s}, {s}", .{ @tagName(extra.op()), @tagName(extra.ordering()) });
323325 }
324326
327 fn writeMemset(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
328 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
329 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
330
331 try w.writeOperand(s, inst, 0, pl_op.operand);
332 try s.writeAll(", ");
333 try w.writeOperand(s, inst, 1, extra.lhs);
334 try s.writeAll(", ");
335 try w.writeOperand(s, inst, 2, extra.rhs);
336 }
337
338 fn writeMemcpy(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
339 const pl_op = w.air.instructions.items(.data)[inst].pl_op;
340 const extra = w.air.extraData(Air.Bin, pl_op.payload).data;
341
342 try w.writeOperand(s, inst, 0, pl_op.operand);
343 try s.writeAll(", ");
344 try w.writeOperand(s, inst, 1, extra.lhs);
345 try s.writeAll(", ");
346 try w.writeOperand(s, inst, 2, extra.rhs);
347 }
348
325349 fn writeConstant(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
326350 const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
327351 const val = w.air.values[ty_pl.payload];
src/print_zir.zig+28-2
......@@ -210,8 +210,6 @@ const Writer = struct {
210210 .mul_add,
211211 .builtin_call,
212212 .field_parent_ptr,
213 .memcpy,
214 .memset,
215213 .builtin_async_call,
216214 => try self.writePlNode(stream, inst),
217215
......@@ -222,6 +220,8 @@ const Writer = struct {
222220 .cmpxchg_strong, .cmpxchg_weak => try self.writeCmpxchg(stream, inst),
223221 .atomic_store => try self.writeAtomicStore(stream, inst),
224222 .atomic_rmw => try self.writeAtomicRmw(stream, inst),
223 .memcpy => try self.writeMemcpy(stream, inst),
224 .memset => try self.writeMemset(stream, inst),
225225
226226 .struct_init_anon,
227227 .struct_init_anon_ref,
......@@ -692,6 +692,32 @@ const Writer = struct {
692692 try self.writeSrc(stream, inst_data.src());
693693 }
694694
695 fn writeMemcpy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
696 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
697 const extra = self.code.extraData(Zir.Inst.Memcpy, inst_data.payload_index).data;
698
699 try self.writeInstRef(stream, extra.dest);
700 try stream.writeAll(", ");
701 try self.writeInstRef(stream, extra.source);
702 try stream.writeAll(", ");
703 try self.writeInstRef(stream, extra.byte_count);
704 try stream.writeAll(") ");
705 try self.writeSrc(stream, inst_data.src());
706 }
707
708 fn writeMemset(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
709 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
710 const extra = self.code.extraData(Zir.Inst.Memset, inst_data.payload_index).data;
711
712 try self.writeInstRef(stream, extra.dest);
713 try stream.writeAll(", ");
714 try self.writeInstRef(stream, extra.byte);
715 try stream.writeAll(", ");
716 try self.writeInstRef(stream, extra.byte_count);
717 try stream.writeAll(") ");
718 try self.writeSrc(stream, inst_data.src());
719 }
720
695721 fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
696722 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
697723 const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
src/type.zig+65-8
......@@ -2391,12 +2391,11 @@ pub const Type = extern union {
23912391 };
23922392 }
23932393
2394 /// Asserts the type is a pointer or array type.
2395 pub fn elemType(self: Type) Type {
2396 return switch (self.tag()) {
2397 .vector => self.castTag(.vector).?.data.elem_type,
2398 .array => self.castTag(.array).?.data.elem_type,
2399 .array_sentinel => self.castTag(.array_sentinel).?.data.elem_type,
2394 pub fn childType(ty: Type) Type {
2395 return switch (ty.tag()) {
2396 .vector => ty.castTag(.vector).?.data.elem_type,
2397 .array => ty.castTag(.array).?.data.elem_type,
2398 .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type,
24002399 .single_const_pointer,
24012400 .single_mut_pointer,
24022401 .many_const_pointer,
......@@ -2405,7 +2404,48 @@ pub const Type = extern union {
24052404 .c_mut_pointer,
24062405 .const_slice,
24072406 .mut_slice,
2408 => self.castPointer().?.data,
2407 => ty.castPointer().?.data,
2408
2409 .array_u8,
2410 .array_u8_sentinel_0,
2411 .const_slice_u8,
2412 .manyptr_u8,
2413 .manyptr_const_u8,
2414 => Type.initTag(.u8),
2415
2416 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
2417 .pointer => ty.castTag(.pointer).?.data.pointee_type,
2418
2419 else => unreachable,
2420 };
2421 }
2422
2423 /// Asserts the type is a pointer or array type.
2424 /// TODO this is deprecated in favor of `childType`.
2425 pub const elemType = childType;
2426
2427 /// For *[N]T, returns T.
2428 /// For ?*T, returns T.
2429 /// For ?*[N]T, returns T.
2430 /// For ?[*]T, returns T.
2431 /// For *T, returns T.
2432 /// For [*]T, returns T.
2433 pub fn elemType2(ty: Type) Type {
2434 return switch (ty.tag()) {
2435 .vector => ty.castTag(.vector).?.data.elem_type,
2436 .array => ty.castTag(.array).?.data.elem_type,
2437 .array_sentinel => ty.castTag(.array_sentinel).?.data.elem_type,
2438 .many_const_pointer,
2439 .many_mut_pointer,
2440 .c_const_pointer,
2441 .c_mut_pointer,
2442 .const_slice,
2443 .mut_slice,
2444 => ty.castPointer().?.data,
2445
2446 .single_const_pointer,
2447 .single_mut_pointer,
2448 => ty.castPointer().?.data.shallowElemType(),
24092449
24102450 .array_u8,
24112451 .array_u8_sentinel_0,
......@@ -2415,12 +2455,29 @@ pub const Type = extern union {
24152455 => Type.initTag(.u8),
24162456
24172457 .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
2418 .pointer => self.castTag(.pointer).?.data.pointee_type,
2458 .pointer => {
2459 const info = ty.castTag(.pointer).?.data;
2460 const child_ty = info.pointee_type;
2461 if (info.size == .One) {
2462 return child_ty.shallowElemType();
2463 } else {
2464 return child_ty;
2465 }
2466 },
2467
2468 // TODO handle optionals
24192469
24202470 else => unreachable,
24212471 };
24222472 }
24232473
2474 fn shallowElemType(child_ty: Type) Type {
2475 return switch (child_ty.zigTypeTag()) {
2476 .Array, .Vector => child_ty.childType(),
2477 else => child_ty,
2478 };
2479 }
2480
24242481 /// Asserts that the type is an optional.
24252482 /// Resulting `Type` will have inner memory referencing `buf`.
24262483 pub fn optionalChild(self: Type, buf: *Payload.ElemType) Type {
test/behavior/basic.zig+18
......@@ -170,3 +170,21 @@ test "string concatenation" {
170170test "array mult operator" {
171171 try expect(mem.eql(u8, "ab" ** 5, "ababababab"));
172172}
173
174test "memcpy and memset intrinsics" {
175 try testMemcpyMemset();
176 // TODO add comptime test coverage
177 //comptime try testMemcpyMemset();
178}
179
180fn testMemcpyMemset() !void {
181 var foo: [20]u8 = undefined;
182 var bar: [20]u8 = undefined;
183
184 @memset(&foo, 'A', foo.len);
185 @memcpy(&bar, &foo, bar.len);
186
187 try expect(bar[0] == 'A');
188 try expect(bar[11] == 'A');
189 try expect(bar[19] == 'A');
190}
test/behavior/misc.zig-10
......@@ -5,16 +5,6 @@ const expectEqualStrings = std.testing.expectEqualStrings;
55const mem = std.mem;
66const builtin = @import("builtin");
77
8test "memcpy and memset intrinsics" {
9 var foo: [20]u8 = undefined;
10 var bar: [20]u8 = undefined;
11
12 @memset(&foo, 'A', foo.len);
13 @memcpy(&bar, &foo, bar.len);
14
15 if (bar[11] != 'A') unreachable;
16}
17
188test "slicing" {
199 var array: [20]i32 = undefined;
2010