aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig245
-rw-r--r--src/codegen/llvm.zig251
2 files changed, 356 insertions, 140 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 4bf6b74adf..385094e495 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -2924,7 +2924,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.load => try airLoad(f, inst),
.ret => try airRet(f, inst, false),
.ret_load => try airRet(f, inst, true),
- .store => try airStore(f, inst),
+ .store => try airStore(f, inst, false),
+ .store_safe => try airStore(f, inst, true),
.loop => try airLoop(f, inst),
.cond_br => try airCondBr(f, inst),
.br => try airBr(f, inst),
@@ -2935,7 +2936,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.cmpxchg_strong => try airCmpxchg(f, inst, "strong"),
.atomic_rmw => try airAtomicRmw(f, inst),
.atomic_load => try airAtomicLoad(f, inst),
- .memset => try airMemset(f, inst),
+ .memset => try airMemset(f, inst, false),
+ .memset_safe => try airMemset(f, inst, true),
.memcpy => try airMemcpy(f, inst),
.set_union_tag => try airSetUnionTag(f, inst),
.get_union_tag => try airGetUnionTag(f, inst),
@@ -3574,19 +3576,7 @@ fn airBoolToInt(f: *Function, inst: Air.Inst.Index) !CValue {
return local;
}
-fn storeUndefined(f: *Function, lhs_child_ty: Type, dest_ptr: CValue) !CValue {
- if (f.wantSafety()) {
- const writer = f.object.writer();
- try writer.writeAll("memset(");
- try f.writeCValue(writer, dest_ptr, .FunctionArgument);
- try writer.print(", {x}, sizeof(", .{try f.fmtIntLiteral(Type.u8, Value.undef)});
- try f.renderType(writer, lhs_child_ty);
- try writer.writeAll("));\n");
- }
- return .none;
-}
-
-fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
+fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
// *a = b;
const bin_op = f.air.instructions.items(.data)[inst].bin_op;
@@ -3597,18 +3587,19 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
const ptr_val = try f.resolveInst(bin_op.lhs);
const src_ty = f.air.typeOf(bin_op.rhs);
- // TODO Sema should emit a different instruction when the store should
- // possibly do the safety 0xaa bytes for undefined.
- const src_val_is_undefined =
- if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
- if (src_val_is_undefined) {
- if (ptr_info.host_size == 0) {
- try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
- return try storeUndefined(f, ptr_info.pointee_type, ptr_val);
- } else if (!f.wantSafety()) {
- try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
- return .none;
+ const val_is_undef = if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
+
+ if (val_is_undef) {
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
+ if (safety and ptr_info.host_size == 0) {
+ const writer = f.object.writer();
+ try writer.writeAll("memset(");
+ try f.writeCValue(writer, ptr_val, .FunctionArgument);
+ try writer.writeAll(", 0xaa, sizeof(");
+ try f.renderType(writer, ptr_info.pointee_type);
+ try writer.writeAll("));\n");
}
+ return .none;
}
const target = f.object.dg.module.getTarget();
@@ -3844,8 +3835,8 @@ fn airCmpOp(
data: anytype,
operator: std.math.CompareOperator,
) !CValue {
- const operand_ty = f.air.typeOf(data.lhs);
- const scalar_ty = operand_ty.scalarType();
+ const lhs_ty = f.air.typeOf(data.lhs);
+ const scalar_ty = lhs_ty.scalarType();
const target = f.object.dg.module.getTarget();
const scalar_bits = scalar_ty.bitSize(target);
@@ -3866,17 +3857,21 @@ fn airCmpOp(
const rhs = try f.resolveInst(data.rhs);
try reap(f, inst, &.{ data.lhs, data.rhs });
+ const rhs_ty = f.air.typeOf(data.rhs);
+ const need_cast = lhs_ty.isSinglePointer() != rhs_ty.isSinglePointer();
const writer = f.object.writer();
const local = try f.allocLocal(inst, inst_ty);
- const v = try Vectorize.start(f, inst, writer, operand_ty);
+ const v = try Vectorize.start(f, inst, writer, lhs_ty);
try f.writeCValue(writer, local, .Other);
try v.elem(f, writer);
try writer.writeAll(" = ");
+ if (need_cast) try writer.writeAll("(void*)");
try f.writeCValue(writer, lhs, .Other);
try v.elem(f, writer);
try writer.writeByte(' ');
try writer.writeAll(compareOperatorC(operator));
try writer.writeByte(' ');
+ if (need_cast) try writer.writeAll("(void*)");
try f.writeCValue(writer, rhs, .Other);
try v.elem(f, writer);
try writer.writeAll(";\n");
@@ -4301,9 +4296,13 @@ fn airBlock(f: *Function, inst: Air.Inst.Index) !CValue {
}
try f.object.indent_writer.insertNewline();
- // label might be unused, add a dummy goto
- // label must be followed by an expression, add an empty one.
- try writer.print("goto zig_block_{d};\nzig_block_{d}: (void)0;\n", .{ block_id, block_id });
+
+ // noreturn blocks have no `br` instructions reaching them, so we don't want a label
+ if (!f.air.typeOfIndex(inst).isNoReturn()) {
+ // label must be followed by an expression, include an empty one.
+ try writer.print("zig_block_{d}:;\n", .{block_id});
+ }
+
return result;
}
@@ -4355,7 +4354,7 @@ fn lowerTry(
else
try f.writeCValueMember(writer, err_union, .{ .identifier = "error" });
}
- try writer.writeByte(')');
+ try writer.writeAll(") ");
try genBodyResolveState(f, inst, liveness_condbr.else_deaths, body, false);
try f.object.indent_writer.insertNewline();
@@ -4427,7 +4426,11 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
const local = try f.allocLocal(inst, dest_ty);
+ // If the assignment looks like 'x = x', we don't need it
+ const can_elide = operand == .local and operand.local == local.new_local;
+
if (operand_ty.isAbiInt() and dest_ty.isAbiInt()) {
+ if (can_elide) return local;
const src_info = dest_ty.intInfo(target);
const dest_info = operand_ty.intInfo(target);
if (src_info.signedness == dest_info.signedness and
@@ -4442,6 +4445,7 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
}
if (dest_ty.isPtrAtRuntime() and operand_ty.isPtrAtRuntime()) {
+ if (can_elide) return local;
try f.writeCValue(writer, local, .Other);
try writer.writeAll(" = (");
try f.renderType(writer, dest_ty);
@@ -5473,6 +5477,12 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
const error_ty = error_union_ty.errorUnionSet();
const payload_ty = error_union_ty.errorUnionPayload();
const local = try f.allocLocal(inst, inst_ty);
+
+ if (!payload_ty.hasRuntimeBits() and operand == .local and operand.local == local.new_local) {
+ // The store will be 'x = x'; elide it.
+ return local;
+ }
+
const writer = f.object.writer();
try f.writeCValue(writer, local, .Other);
try writer.writeAll(" = ");
@@ -5570,6 +5580,12 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
const writer = f.object.writer();
const local = try f.allocLocal(inst, inst_ty);
+
+ if (repr_is_err and err == .local and err.local == local.new_local) {
+ // The store will be 'x = x'; elide it.
+ return local;
+ }
+
if (!repr_is_err) {
const a = try Assignment.start(f, writer, payload_ty);
try f.writeCValueMember(writer, local, .{ .identifier = "payload" });
@@ -5784,6 +5800,7 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
const un_op = f.air.instructions.items(.data)[inst].un_op;
const operand = try f.resolveInst(un_op);
+ const operand_ty = f.air.typeOf(un_op);
try reap(f, inst, &.{un_op});
const inst_ty = f.air.typeOfIndex(inst);
const writer = f.object.writer();
@@ -5793,7 +5810,11 @@ fn airPtrToInt(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll(" = (");
try f.renderType(writer, inst_ty);
try writer.writeByte(')');
- try f.writeCValue(writer, operand, .Other);
+ if (operand_ty.isSlice()) {
+ try f.writeCValueMember(writer, operand, .{ .identifier = "len" });
+ } else {
+ try f.writeCValue(writer, operand, .Other);
+ }
try writer.writeAll(";\n");
return local;
}
@@ -6186,19 +6207,66 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa
return .none;
}
-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_ty = f.air.typeOf(pl_op.operand);
- const dest_ptr = try f.resolveInst(pl_op.operand);
- const value = try f.resolveInst(extra.lhs);
- const len = try f.resolveInst(extra.rhs);
+fn writeSliceOrPtr(f: *Function, writer: anytype, ptr: CValue, ptr_ty: Type) !void {
+ if (ptr_ty.isSlice()) {
+ try f.writeCValueMember(writer, ptr, .{ .identifier = "ptr" });
+ } else {
+ try f.writeCValue(writer, ptr, .FunctionArgument);
+ }
+}
+fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue {
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const dest_ty = f.air.typeOf(bin_op.lhs);
+ const dest_slice = try f.resolveInst(bin_op.lhs);
+ const value = try f.resolveInst(bin_op.rhs);
+ const elem_ty = f.air.typeOf(bin_op.rhs);
+ const target = f.object.dg.module.getTarget();
+ const elem_abi_size = elem_ty.abiSize(target);
+ const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
const writer = f.object.writer();
- if (dest_ty.isVolatilePtr()) {
- var u8_ptr_pl = dest_ty.ptrInfo();
- u8_ptr_pl.data.pointee_type = Type.u8;
- const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base);
+
+ if (val_is_undef) {
+ if (!safety) {
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
+ return .none;
+ }
+
+ try writer.writeAll("memset(");
+ switch (dest_ty.ptrSize()) {
+ .Slice => {
+ try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
+ try writer.writeAll(", 0xaa, ");
+ try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
+ if (elem_abi_size > 1) {
+ try writer.print(" * {d});\n", .{elem_abi_size});
+ } else {
+ try writer.writeAll(");\n");
+ }
+ },
+ .One => {
+ const array_ty = dest_ty.childType();
+ const len = array_ty.arrayLen() * elem_abi_size;
+
+ try f.writeCValue(writer, dest_slice, .FunctionArgument);
+ try writer.print(", 0xaa, {d});\n", .{len});
+ },
+ .Many, .C => unreachable,
+ }
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
+ return .none;
+ }
+
+ if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) {
+ // For the assignment in this loop, the array pointer needs to get
+ // casted to a regular pointer, otherwise an error like this occurs:
+ // error: array type 'uint32_t[20]' (aka 'unsigned int[20]') is not assignable
+ var elem_ptr_ty_pl: Type.Payload.ElemType = .{
+ .base = .{ .tag = .c_mut_pointer },
+ .data = elem_ty,
+ };
+ const elem_ptr_ty = Type.initPayload(&elem_ptr_ty_pl.base);
+
const index = try f.allocLocal(inst, Type.usize);
try writer.writeAll("for (");
@@ -6208,56 +6276,95 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue {
try writer.writeAll("; ");
try f.writeCValue(writer, index, .Other);
try writer.writeAll(" != ");
- try f.writeCValue(writer, len, .Other);
- try writer.writeAll("; ");
+ switch (dest_ty.ptrSize()) {
+ .Slice => {
+ try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
+ },
+ .One => {
+ const array_ty = dest_ty.childType();
+ try writer.print("{d}", .{array_ty.arrayLen()});
+ },
+ .Many, .C => unreachable,
+ }
+ try writer.writeAll("; ++");
try f.writeCValue(writer, index, .Other);
- try writer.writeAll(" += ");
- try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other);
try writer.writeAll(") ((");
- try f.renderType(writer, u8_ptr_ty);
+ try f.renderType(writer, elem_ptr_ty);
try writer.writeByte(')');
- try f.writeCValue(writer, dest_ptr, .FunctionArgument);
+ try writeSliceOrPtr(f, writer, dest_slice, dest_ty);
try writer.writeAll(")[");
try f.writeCValue(writer, index, .Other);
try writer.writeAll("] = ");
try f.writeCValue(writer, value, .FunctionArgument);
try writer.writeAll(";\n");
- try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
try freeLocal(f, inst, index.new_local, 0);
return .none;
}
- try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
try writer.writeAll("memset(");
- try f.writeCValue(writer, dest_ptr, .FunctionArgument);
- try writer.writeAll(", ");
- try f.writeCValue(writer, value, .FunctionArgument);
- try writer.writeAll(", ");
- try f.writeCValue(writer, len, .FunctionArgument);
- try writer.writeAll(");\n");
+ switch (dest_ty.ptrSize()) {
+ .Slice => {
+ try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" });
+ try writer.writeAll(", ");
+ try f.writeCValue(writer, value, .FunctionArgument);
+ try writer.writeAll(", ");
+ try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" });
+ try writer.writeAll(");\n");
+ },
+ .One => {
+ const array_ty = dest_ty.childType();
+ const len = array_ty.arrayLen() * elem_abi_size;
+ try f.writeCValue(writer, dest_slice, .FunctionArgument);
+ try writer.writeAll(", ");
+ try f.writeCValue(writer, value, .FunctionArgument);
+ try writer.print(", {d});\n", .{len});
+ },
+ .Many, .C => unreachable,
+ }
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
return .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);
- try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs });
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const dest_ptr = try f.resolveInst(bin_op.lhs);
+ const src_ptr = try f.resolveInst(bin_op.rhs);
+ const dest_ty = f.air.typeOf(bin_op.lhs);
+ const src_ty = f.air.typeOf(bin_op.rhs);
+ const target = f.object.dg.module.getTarget();
const writer = f.object.writer();
try writer.writeAll("memcpy(");
- try f.writeCValue(writer, dest_ptr, .FunctionArgument);
+ try writeSliceOrPtr(f, writer, dest_ptr, dest_ty);
try writer.writeAll(", ");
- try f.writeCValue(writer, src_ptr, .FunctionArgument);
+ try writeSliceOrPtr(f, writer, src_ptr, src_ty);
try writer.writeAll(", ");
- try f.writeCValue(writer, len, .FunctionArgument);
- try writer.writeAll(");\n");
+ switch (dest_ty.ptrSize()) {
+ .Slice => {
+ const elem_ty = dest_ty.childType();
+ const elem_abi_size = elem_ty.abiSize(target);
+ try f.writeCValueMember(writer, dest_ptr, .{ .identifier = "len" });
+ if (elem_abi_size > 1) {
+ try writer.print(" * {d});\n", .{elem_abi_size});
+ } else {
+ try writer.writeAll(");\n");
+ }
+ },
+ .One => {
+ const array_ty = dest_ty.childType();
+ const elem_ty = array_ty.childType();
+ const elem_abi_size = elem_ty.abiSize(target);
+ const len = array_ty.arrayLen() * elem_abi_size;
+ try writer.print("{d});\n", .{len});
+ },
+ .Many, .C => unreachable,
+ }
+ try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
return .none;
}
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 76dabd3e9b..94f49e801d 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -4649,7 +4649,8 @@ pub const FuncGen = struct {
.not => try self.airNot(inst),
.ret => try self.airRet(inst),
.ret_load => try self.airRetLoad(inst),
- .store => try self.airStore(inst),
+ .store => try self.airStore(inst, false),
+ .store_safe => try self.airStore(inst, true),
.assembly => try self.airAssembly(inst),
.slice_ptr => try self.airSliceField(inst, 0),
.slice_len => try self.airSliceField(inst, 1),
@@ -4672,7 +4673,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),
+ .memset => try self.airMemset(inst, false),
+ .memset_safe => try self.airMemset(inst, true),
.memcpy => try self.airMemcpy(inst),
.set_union_tag => try self.airSetUnionTag(inst),
.get_union_tag => try self.airGetUnionTag(inst),
@@ -5776,6 +5778,36 @@ pub const FuncGen = struct {
return result;
}
+ fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value {
+ if (ty.isSlice()) {
+ return fg.builder.buildExtractValue(ptr, 0, "");
+ } else {
+ return ptr;
+ }
+ }
+
+ 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);
@@ -7261,39 +7293,53 @@ pub const FuncGen = struct {
fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
- const base_ptr = try self.resolveInst(bin_op.lhs);
+ const ptr = try self.resolveInst(bin_op.lhs);
const offset = try self.resolveInst(bin_op.rhs);
const ptr_ty = self.air.typeOf(bin_op.lhs);
const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
- if (ptr_ty.ptrSize() == .One) {
- // It's a pointer to an array, so according to LLVM we need an extra GEP index.
- const indices: [2]*llvm.Value = .{
- self.context.intType(32).constNull(), offset,
- };
- return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
- } else {
- const indices: [1]*llvm.Value = .{offset};
- return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
+ switch (ptr_ty.ptrSize()) {
+ .One => {
+ // It's a pointer to an array, so according to LLVM we need an extra GEP index.
+ const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
+ },
+ .C, .Many => {
+ const indices: [1]*llvm.Value = .{offset};
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
+ },
+ .Slice => {
+ const base = self.builder.buildExtractValue(ptr, 0, "");
+ const indices: [1]*llvm.Value = .{offset};
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
+ },
}
}
fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
- const base_ptr = try self.resolveInst(bin_op.lhs);
+ const ptr = try self.resolveInst(bin_op.lhs);
const offset = try self.resolveInst(bin_op.rhs);
const negative_offset = self.builder.buildNeg(offset, "");
const ptr_ty = self.air.typeOf(bin_op.lhs);
const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
- if (ptr_ty.ptrSize() == .One) {
- // It's a pointer to an array, so according to LLVM we need an extra GEP index.
- const indices: [2]*llvm.Value = .{
- self.context.intType(32).constNull(), negative_offset,
- };
- return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
- } else {
- const indices: [1]*llvm.Value = .{negative_offset};
- return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
+ switch (ptr_ty.ptrSize()) {
+ .One => {
+ // It's a pointer to an array, so according to LLVM we need an extra GEP index.
+ const indices: [2]*llvm.Value = .{
+ self.context.intType(32).constNull(), negative_offset,
+ };
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
+ },
+ .C, .Many => {
+ const indices: [1]*llvm.Value = .{negative_offset};
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
+ },
+ .Slice => {
+ const base = self.builder.buildExtractValue(ptr, 0, "");
+ const indices: [1]*llvm.Value = .{negative_offset};
+ return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
+ },
}
}
@@ -7887,8 +7933,10 @@ pub const FuncGen = struct {
fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
const un_op = self.air.instructions.items(.data)[inst].un_op;
const operand = try self.resolveInst(un_op);
+ const ptr_ty = self.air.typeOf(un_op);
+ const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty);
const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst));
- return self.builder.buildPtrToInt(operand, dest_llvm_ty, "");
+ return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, "");
}
fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
@@ -8082,48 +8130,36 @@ pub const FuncGen = struct {
return buildAllocaInner(self.context, self.builder, self.llvm_func, self.di_scope != null, llvm_ty, alignment, self.dg.module.getTarget());
}
- fn airStore(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
+ fn airStore(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.Value {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const dest_ptr = try self.resolveInst(bin_op.lhs);
const ptr_ty = self.air.typeOf(bin_op.lhs);
const operand_ty = ptr_ty.childType();
- // TODO Sema should emit a different instruction when the store should
- // possibly do the safety 0xaa bytes for undefined.
const val_is_undef = if (self.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false;
if (val_is_undef) {
- {
- // TODO let's handle this in AIR rather than by having each backend
- // check the optimization mode of the compilation because the plan is
- // to support setting the optimization mode at finer grained scopes
- // which happens in Sema. Codegen should not be aware of this logic.
- // I think this comment is basically the same as the other TODO comment just
- // above but I'm leaving them both here to make it look super messy and
- // thereby bait contributors (or let's be honest, probably myself) into
- // fixing this instead of letting it rot.
- const safety = switch (self.dg.module.comp.bin_file.options.optimize_mode) {
- .ReleaseSmall, .ReleaseFast => false,
- .Debug, .ReleaseSafe => true,
- };
- if (!safety) {
- return null;
- }
- }
+ // Even if safety is disabled, we still emit a memset to undefined since it conveys
+ // extra information to LLVM. However, safety makes the difference between using
+ // 0xaa or actual undefined for the fill byte.
+ const u8_llvm_ty = self.context.intType(8);
+ const fill_byte = if (safety)
+ u8_llvm_ty.constInt(0xaa, .False)
+ else
+ u8_llvm_ty.getUndef();
const target = self.dg.module.getTarget();
const operand_size = operand_ty.abiSize(target);
- const u8_llvm_ty = self.context.intType(8);
- const fill_char = u8_llvm_ty.constInt(0xaa, .False);
- const dest_ptr_align = ptr_ty.ptrAlignment(target);
const usize_llvm_ty = try self.dg.lowerType(Type.usize);
const len = usize_llvm_ty.constInt(operand_size, .False);
- _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
- if (self.dg.module.comp.bin_file.options.valgrind) {
+ const dest_ptr_align = ptr_ty.ptrAlignment(target);
+ _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+ if (safety and self.dg.module.comp.bin_file.options.valgrind) {
self.valgrindMarkUndef(dest_ptr, len);
}
- } else {
- const src_operand = try self.resolveInst(bin_op.rhs);
- try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
+ return null;
}
+
+ const src_operand = try self.resolveInst(bin_op.rhs);
+ try self.store(dest_ptr, ptr_ty, src_operand, .NotAtomic);
return null;
}
@@ -8373,34 +8409,107 @@ pub const FuncGen = struct {
return null;
}
- 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;
+ fn airMemset(self: *FuncGen, inst: Air.Inst.Index, safety: bool) !?*llvm.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 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 dest_ptr_align = ptr_ty.ptrAlignment(target);
- _ = self.builder.buildMemSet(dest_ptr, fill_char, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+ const u8_llvm_ty = self.context.intType(8);
+ const dest_ptr = self.sliceOrArrayPtr(dest_slice, ptr_ty);
+
+ if (val_is_undef) {
+ // Even if safety is disabled, we still emit a memset to undefined since it conveys
+ // extra information to LLVM. However, safety makes the difference between using
+ // 0xaa or actual undefined for the fill byte.
+ const fill_byte = if (safety)
+ u8_llvm_ty.constInt(0xaa, .False)
+ else
+ u8_llvm_ty.getUndef();
+ const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
+ _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+
+ if (safety and self.dg.module.comp.bin_file.options.valgrind) {
+ self.valgrindMarkUndef(dest_ptr, len);
+ }
+ return null;
+ }
+
+ const value = try self.resolveInst(bin_op.rhs);
+ const elem_abi_size = elem_ty.abiSize(target);
- if (val_is_undef and self.dg.module.comp.bin_file.options.valgrind) {
- self.valgrindMarkUndef(dest_ptr, len);
+ if (elem_abi_size == 1) {
+ // In this case we can take advantage of LLVM's intrinsic.
+ const fill_byte = self.builder.buildBitCast(value, u8_llvm_ty, "");
+ const len = self.sliceOrArrayLenInBytes(dest_slice, ptr_ty);
+ _ = self.builder.buildMemSet(dest_ptr, fill_byte, len, dest_ptr_align, ptr_ty.isVolatilePtr());
+ return null;
}
+
+ // non-byte-sized element. lower with a loop. something like this:
+
+ // entry:
+ // ...
+ // %end_ptr = getelementptr %ptr, %len
+ // br loop
+ // loop:
+ // %it_ptr = phi body %next_ptr, entry %ptr
+ // %end = cmp eq %it_ptr, %end_ptr
+ // cond_br %end body, end
+ // body:
+ // store %it_ptr, %value
+ // %next_ptr = getelementptr %it_ptr, 1
+ // br loop
+ // end:
+ // ...
+ const entry_block = self.builder.getInsertBlock();
+ const loop_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetLoop");
+ const body_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetBody");
+ const end_block = self.context.appendBasicBlock(self.llvm_func, "InlineMemsetEnd");
+
+ const llvm_usize_ty = self.context.intType(target.cpu.arch.ptrBitWidth());
+ const len = switch (ptr_ty.ptrSize()) {
+ .Slice => self.builder.buildExtractValue(dest_slice, 1, ""),
+ .One => llvm_usize_ty.constInt(ptr_ty.childType().arrayLen(), .False),
+ .Many, .C => unreachable,
+ };
+ const elem_llvm_ty = try self.dg.lowerType(elem_ty);
+ const len_gep = [_]*llvm.Value{len};
+ const end_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, dest_ptr, &len_gep, len_gep.len, "");
+ _ = self.builder.buildBr(loop_block);
+
+ self.builder.positionBuilderAtEnd(loop_block);
+ const it_ptr = self.builder.buildPhi(self.context.pointerType(0), "");
+ const end = self.builder.buildICmp(.NE, it_ptr, end_ptr, "");
+ _ = self.builder.buildCondBr(end, body_block, end_block);
+
+ self.builder.positionBuilderAtEnd(body_block);
+ const store_inst = self.builder.buildStore(value, it_ptr);
+ store_inst.setAlignment(@min(elem_ty.abiAlignment(target), dest_ptr_align));
+ const one_gep = [_]*llvm.Value{llvm_usize_ty.constInt(1, .False)};
+ const next_ptr = self.builder.buildInBoundsGEP(elem_llvm_ty, it_ptr, &one_gep, one_gep.len, "");
+ _ = self.builder.buildBr(loop_block);
+
+ self.builder.positionBuilderAtEnd(end_block);
+
+ const incoming_values: [2]*llvm.Value = .{ next_ptr, dest_ptr };
+ const incoming_blocks: [2]*llvm.BasicBlock = .{ body_block, entry_block };
+ it_ptr.addIncoming(&incoming_values, &incoming_blocks, 2);
+
return null;
}
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(