diff options
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 245 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 251 |
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( |
