aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-04-13 21:44:40 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-04-25 11:23:40 -0700
commita5c910adb610ae530db99f10aa77aaed3e85e830 (patch)
tree5c3f72dbac50fc9f09608be3d7ea328c629c00a0 /src/codegen/llvm.zig
parent8d88dcdc61c61e3410138f4402482131f5074a80 (diff)
downloadzig-a5c910adb610ae530db99f10aa77aaed3e85e830.tar.gz
zig-a5c910adb610ae530db99f10aa77aaed3e85e830.zip
change semantics of `@memcpy` and `@memset`
Now they use slices or array pointers with any element type instead of requiring byte pointers. This is a breaking enhancement to the language. The safety check for overlapping pointers will be implemented in a future commit. closes #14040
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig71
1 files changed, 54 insertions, 17 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 76dabd3e9b..d80fb62969 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -5776,6 +5776,36 @@ pub const FuncGen = struct {
return result;
}
+ fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
+ switch (ty.ptrSize()) {
+ .Slice => return fg.builder.buildExtractValue(ptr, 0, ""),
+ .One => return ptr,
+ .Many, .C => unreachable,
+ }
+ }
+
+ fn sliceOrArrayLenInBytes(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
+ const target = fg.dg.module.getTarget();
+ const llvm_usize_ty = fg.context.intType(target.cpu.arch.ptrBitWidth());
+ switch (ty.ptrSize()) {
+ .Slice => {
+ const len = fg.builder.buildExtractValue(ptr, 1, "");
+ const elem_ty = ty.childType();
+ const abi_size = elem_ty.abiSize(target);
+ if (abi_size == 1) return len;
+ const abi_size_llvm_val = llvm_usize_ty.constInt(abi_size, .False);
+ return fg.builder.buildMul(len, abi_size_llvm_val, "");
+ },
+ .One => {
+ const array_ty = ty.childType();
+ const elem_ty = array_ty.childType();
+ const abi_size = elem_ty.abiSize(target);
+ return llvm_usize_ty.constInt(array_ty.arrayLen() * abi_size, .False);
+ },
+ .Many, .C => unreachable,
+ }
+ }
+
fn airSliceField(self: *FuncGen, inst: Air.Inst.Index, index: c_uint) !?*llvm.Value {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const operand = try self.resolveInst(ty_op.operand);
@@ -8374,18 +8404,24 @@ pub const FuncGen = struct {
}
fn airMemset(self: *FuncGen, inst: Air.Inst.Index) !?*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.isUndefDeep() else false;
- const len = try self.resolveInst(extra.rhs);
- const u8_llvm_ty = self.context.intType(8);
- const fill_char = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else value;
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const dest_slice = try self.resolveInst(bin_op.lhs);
+ const ptr_ty = self.air.typeOf(bin_op.lhs);
+ const value = try self.resolveInst(bin_op.rhs);
+ const elem_ty = self.air.typeOf(bin_op.rhs);
const target = self.dg.module.getTarget();
+ const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
+ const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
+ const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty);
+ const u8_llvm_ty = self.context.intType(8);
+ const fill_byte = if (val_is_undef) u8_llvm_ty.constInt(0xaa, .False) else b: {
+ if (elem_ty.abiSize(target) != 1) {
+ return self.dg.todo("implement @memset for non-byte-sized element type", .{});
+ }
+ break :b self.builder.buildBitCast(value, u8_llvm_ty, "");
+ };
const dest_ptr_align = ptr_ty.ptrAlignment(target);
- _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+ _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
self.valgrindMarkUndef(dest_ptr, len);
@@ -8394,13 +8430,14 @@ pub const FuncGen = struct {
}
fn airMemcpy(self: *FuncGen, inst: Air.Inst.Index) !?*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 bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const dest_slice = try self.resolveInst(bin_op.lhs);
+ const dest_ptr_ty = self.air.typeOf(bin_op.lhs);
+ const src_slice = try self.resolveInst(bin_op.rhs);
+ const src_ptr_ty = self.air.typeOf(bin_op.rhs);
+ const src_ptr = self.sliceOrArrayPtr(src_slice, src_ptr_ty);
+ const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
+ const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
const is_volatile = src_ptr_ty.isVolatilePtr() or dest_ptr_ty.isVolatilePtr();
const target = self.dg.module.getTarget();
_ = self.builder.buildMemCpy(