aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-01-18 10:49:54 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-01-18 11:59:09 -0700
commit30efcf22d799c055d8ec987aa2f55a11c133b709 (patch)
tree35febf72fdea7d3f571f01005823442701e02dd0
parentf423b5949b8722d4b290f57c3d06d015e39217b0 (diff)
downloadzig-30efcf22d799c055d8ec987aa2f55a11c133b709.tar.gz
zig-30efcf22d799c055d8ec987aa2f55a11c133b709.zip
stage2: implement `@prefetch`
This reverts commit f423b5949b8722d4b290f57c3d06d015e39217b0, re-instating commit d48e4245b68bf25c7f41804a5012ac157a5ee546.
-rw-r--r--lib/std/builtin.zig4
-rw-r--r--src/Air.zig12
-rw-r--r--src/Liveness.zig5
-rw-r--r--src/Sema.zig120
-rw-r--r--src/arch/aarch64/CodeGen.zig6
-rw-r--r--src/arch/arm/CodeGen.zig6
-rw-r--r--src/arch/riscv64/CodeGen.zig6
-rw-r--r--src/arch/wasm/CodeGen.zig67
-rw-r--r--src/arch/x86_64/CodeGen.zig6
-rw-r--r--src/codegen/c.zig20
-rw-r--r--src/codegen/llvm.zig62
-rw-r--r--src/link/C/zig.h6
-rw-r--r--src/print_air.zig10
-rw-r--r--test/behavior.zig2
14 files changed, 287 insertions, 45 deletions
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 72503b7d2f..ba5801e936 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -643,12 +643,12 @@ pub const PrefetchOptions = struct {
/// The cache that the prefetch should be preformed on.
cache: Cache = .data,
- pub const Rw = enum {
+ pub const Rw = enum(u1) {
read,
write,
};
- pub const Cache = enum {
+ pub const Cache = enum(u1) {
instruction,
data,
};
diff --git a/src/Air.zig b/src/Air.zig
index 274c30167a..0f9542af1b 100644
--- a/src/Air.zig
+++ b/src/Air.zig
@@ -515,6 +515,11 @@ pub const Inst = struct {
/// is a `Ref`. Length of the array is given by the vector type.
vector_init,
+ /// Communicates an intent to load memory.
+ /// Result is always unused.
+ /// Uses the `prefetch` field.
+ prefetch,
+
pub fn fromCmpOp(op: std.math.CompareOperator) Tag {
return switch (op) {
.lt => .cmp_lt,
@@ -586,6 +591,12 @@ pub const Inst = struct {
ptr: Ref,
order: std.builtin.AtomicOrder,
},
+ prefetch: struct {
+ ptr: Ref,
+ rw: std.builtin.PrefetchOptions.Rw,
+ locality: u2,
+ cache: std.builtin.PrefetchOptions.Cache,
+ },
// Make sure we don't accidentally add a field to make this union
// bigger than expected. Note that in Debug builds, Zig is allowed
@@ -823,6 +834,7 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.memset,
.memcpy,
.set_union_tag,
+ .prefetch,
=> return Type.initTag(.void),
.ptrtoint,
diff --git a/src/Liveness.zig b/src/Liveness.zig
index 92ec51e574..1bdbab6788 100644
--- a/src/Liveness.zig
+++ b/src/Liveness.zig
@@ -342,6 +342,11 @@ fn analyzeInst(
return trackOperands(a, new_set, inst, main_tomb, .{ operand, .none, .none });
},
+ .prefetch => {
+ const prefetch = inst_datas[inst].prefetch;
+ return trackOperands(a, new_set, inst, main_tomb, .{ prefetch.ptr, .none, .none });
+ },
+
.call => {
const inst_data = inst_datas[inst].pl_op;
const callee = inst_data.operand;
diff --git a/src/Sema.zig b/src/Sema.zig
index 616ec3e7ac..82af87bd4d 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -10316,49 +10316,53 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
const obj_ty = try sema.resolveType(block, src, inst_data.operand);
- const gpa = sema.gpa;
switch (obj_ty.zigTypeTag()) {
- .Struct => {
- // This logic must be synchronized with that in `zirStructInit`.
- const struct_ty = try sema.resolveTypeFields(block, src, obj_ty);
- const struct_obj = struct_ty.castTag(.@"struct").?.data;
-
- // The init values to use for the struct instance.
- const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
- defer gpa.free(field_inits);
-
- var root_msg: ?*Module.ErrorMsg = null;
-
- for (struct_obj.fields.values()) |field, i| {
- if (field.default_val.tag() == .unreachable_value) {
- const field_name = struct_obj.fields.keys()[i];
- const template = "missing struct field: {s}";
- const args = .{field_name};
- if (root_msg) |msg| {
- try sema.errNote(block, src, msg, template, args);
- } else {
- root_msg = try sema.errMsg(block, src, template, args);
- }
- } else {
- field_inits[i] = try sema.addConstant(field.ty, field.default_val);
- }
- }
- return sema.finishStructInit(block, src, field_inits, root_msg, struct_obj, struct_ty, false);
- },
- .Array => {
- if (obj_ty.sentinel()) |sentinel| {
- const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);
- return sema.addConstant(obj_ty, val);
- } else {
- return sema.addConstant(obj_ty, Value.initTag(.empty_array));
- }
- },
+ .Struct => return structInitEmpty(sema, block, obj_ty, src, src),
+ .Array => return arrayInitEmpty(sema, obj_ty),
.Void => return sema.addConstant(obj_ty, Value.void),
else => unreachable,
}
}
+fn structInitEmpty(sema: *Sema, block: *Block, obj_ty: Type, dest_src: LazySrcLoc, init_src: LazySrcLoc) CompileError!Air.Inst.Ref {
+ const gpa = sema.gpa;
+ // This logic must be synchronized with that in `zirStructInit`.
+ const struct_ty = try sema.resolveTypeFields(block, dest_src, obj_ty);
+ const struct_obj = struct_ty.castTag(.@"struct").?.data;
+
+ // The init values to use for the struct instance.
+ const field_inits = try gpa.alloc(Air.Inst.Ref, struct_obj.fields.count());
+ defer gpa.free(field_inits);
+
+ var root_msg: ?*Module.ErrorMsg = null;
+
+ for (struct_obj.fields.values()) |field, i| {
+ if (field.default_val.tag() == .unreachable_value) {
+ const field_name = struct_obj.fields.keys()[i];
+ const template = "missing struct field: {s}";
+ const args = .{field_name};
+ if (root_msg) |msg| {
+ try sema.errNote(block, init_src, msg, template, args);
+ } else {
+ root_msg = try sema.errMsg(block, init_src, template, args);
+ }
+ } else {
+ field_inits[i] = try sema.addConstant(field.ty, field.default_val);
+ }
+ }
+ return sema.finishStructInit(block, dest_src, field_inits, root_msg, struct_obj, struct_ty, false);
+}
+
+fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {
+ if (obj_ty.sentinel()) |sentinel| {
+ const val = try Value.Tag.empty_array_sentinel.create(sema.arena, sentinel);
+ return sema.addConstant(obj_ty, val);
+ } else {
+ return sema.addConstant(obj_ty, Value.initTag(.empty_array));
+ }
+}
+
fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
@@ -12326,8 +12330,38 @@ fn zirPrefetch(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
- const src: LazySrcLoc = .{ .node_offset = extra.node };
- return sema.fail(block, src, "TODO: implement Sema.zirPrefetch", .{});
+ const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
+ const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
+ const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions");
+ const ptr = sema.resolveInst(extra.lhs);
+ try sema.checkPtrType(block, ptr_src, sema.typeOf(ptr));
+ const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src);
+
+ const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src);
+ const rw_val = try sema.resolveConstValue(block, opts_src, rw);
+ const rw_tag = rw_val.toEnum(std.builtin.PrefetchOptions.Rw);
+
+ const locality = try sema.fieldVal(block, opts_src, options, "locality", opts_src);
+ const locality_val = try sema.resolveConstValue(block, opts_src, locality);
+ const locality_int = @intCast(u2, locality_val.toUnsignedInt());
+
+ const cache = try sema.fieldVal(block, opts_src, options, "cache", opts_src);
+ const cache_val = try sema.resolveConstValue(block, opts_src, cache);
+ const cache_tag = cache_val.toEnum(std.builtin.PrefetchOptions.Cache);
+
+ if (!block.is_comptime) {
+ _ = try block.addInst(.{
+ .tag = .prefetch,
+ .data = .{ .prefetch = .{
+ .ptr = ptr,
+ .rw = rw_tag,
+ .locality = locality_int,
+ .cache = cache_tag,
+ } },
+ });
+ }
+
+ return Air.Inst.Ref.void_value;
}
fn zirBuiltinExtern(
@@ -13787,6 +13821,11 @@ fn coerce(
},
.Array => switch (inst_ty.zigTypeTag()) {
.Vector => return sema.coerceVectorInMemory(block, dest_ty, dest_ty_src, inst, inst_src),
+ .Struct => {
+ if (inst == .empty_struct) {
+ return arrayInitEmpty(sema, dest_ty);
+ }
+ },
else => {},
},
.Vector => switch (inst_ty.zigTypeTag()) {
@@ -13794,6 +13833,11 @@ fn coerce(
.Vector => return sema.coerceVectors(block, dest_ty, dest_ty_src, inst, inst_src),
else => {},
},
+ .Struct => {
+ if (inst == .empty_struct) {
+ return structInitEmpty(sema, block, dest_ty, dest_ty_src, inst_src);
+ }
+ },
else => {},
}
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig
index f8a774af3b..d5993ea5d7 100644
--- a/src/arch/aarch64/CodeGen.zig
+++ b/src/arch/aarch64/CodeGen.zig
@@ -595,6 +595,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
.vector_init => try self.airVectorInit(inst),
+ .prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2597,6 +2598,11 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
+}
+
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig
index cae5164491..66c5a7a429 100644
--- a/src/arch/arm/CodeGen.zig
+++ b/src/arch/arm/CodeGen.zig
@@ -586,6 +586,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
.vector_init => try self.airVectorInit(inst),
+ .prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -3693,6 +3694,11 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
+}
+
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);
diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig
index c5f0d4fb25..0c310d5680 100644
--- a/src/arch/riscv64/CodeGen.zig
+++ b/src/arch/riscv64/CodeGen.zig
@@ -574,6 +574,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
.vector_init => try self.airVectorInit(inst),
+ .prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -2096,6 +2097,11 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
+}
+
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index 7a8efdfcc2..46a8e114e6 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1297,6 +1297,9 @@ fn copyLocal(self: *Self, value: WValue, ty: Type) InnerError!WValue {
fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
const air_tags = self.air.instructions.items(.tag);
return switch (air_tags[inst]) {
+ .constant => unreachable,
+ .const_ty => unreachable,
+
.add => self.airBinOp(inst, .add),
.addwrap => self.airWrapBinOp(inst, .add),
.sub => self.airBinOp(inst, .sub),
@@ -1330,7 +1333,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.bool_to_int => self.airBoolToInt(inst),
.call => self.airCall(inst),
.cond_br => self.airCondBr(inst),
- .constant => unreachable,
.dbg_stmt => WValue.none,
.intcast => self.airIntcast(inst),
.float_to_int => self.airFloatToInt(inst),
@@ -1358,6 +1360,9 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.ret => self.airRet(inst),
.ret_ptr => self.airRetPtr(inst),
.ret_load => self.airRetLoad(inst),
+ .splat => self.airSplat(inst),
+ .vector_init => self.airVectorInit(inst),
+ .prefetch => self.airPrefetch(inst),
.slice => self.airSlice(inst),
.slice_len => self.airSliceLen(inst),
@@ -1382,7 +1387,55 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.unwrap_errunion_err => self.airUnwrapErrUnionError(inst),
.wrap_errunion_payload => self.airWrapErrUnionPayload(inst),
.wrap_errunion_err => self.airWrapErrUnionErr(inst),
- else => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
+
+ .add_sat,
+ .sub_sat,
+ .mul_sat,
+ .div_float,
+ .div_floor,
+ .div_exact,
+ .rem,
+ .mod,
+ .max,
+ .min,
+ .assembly,
+ .shl_exact,
+ .shl_sat,
+ .ret_addr,
+ .clz,
+ .ctz,
+ .popcount,
+ .is_err_ptr,
+ .is_non_err_ptr,
+ .fptrunc,
+ .fpext,
+ .unwrap_errunion_payload_ptr,
+ .unwrap_errunion_err_ptr,
+ .set_union_tag,
+ .get_union_tag,
+ .ptr_slice_len_ptr,
+ .ptr_slice_ptr_ptr,
+ .int_to_float,
+ .memcpy,
+ .cmpxchg_weak,
+ .cmpxchg_strong,
+ .fence,
+ .atomic_load,
+ .atomic_store_unordered,
+ .atomic_store_monotonic,
+ .atomic_store_release,
+ .atomic_store_seq_cst,
+ .atomic_rmw,
+ .tag_name,
+ .error_name,
+
+ // For these 4, probably best to wait until https://github.com/ziglang/zig/issues/10248
+ // is implemented in the frontend before implementing them here in the wasm backend.
+ .add_with_overflow,
+ .sub_with_overflow,
+ .mul_with_overflow,
+ .shl_with_overflow,
+ => |tag| self.fail("TODO: Implement wasm inst: {s}", .{@tagName(tag)}),
};
}
@@ -3211,7 +3264,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
return result;
}
-fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
+fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
@@ -3222,7 +3275,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO: Implement wasm airSplat", .{});
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
+fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
const vector_ty = self.air.typeOfIndex(inst);
@@ -3234,6 +3287,12 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO: Wasm backend: implement airVectorInit", .{});
}
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ _ = prefetch;
+ return WValue{ .none = {} };
+}
+
fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
assert(operand_ty.hasCodeGenBits());
assert(op == .eq or op == .neq);
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index a68674501c..2cf585fe4e 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -638,6 +638,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
.vector_init => try self.airVectorInit(inst),
+ .prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -3746,6 +3747,11 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
+}
+
fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
// First section of indexes correspond to a set number of constant values.
const ref_int = @enumToInt(inst);
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 3f3147ca80..42778bccf8 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -1278,6 +1278,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.error_name => try airErrorName(f, inst),
.splat => try airSplat(f, inst),
.vector_init => try airVectorInit(f, inst),
+ .prefetch => try airPrefetch(f, inst),
.int_to_float,
.float_to_int,
@@ -3089,6 +3090,25 @@ fn airVectorInit(f: *Function, inst: Air.Inst.Index) !CValue {
return f.fail("TODO: C backend: implement airVectorInit", .{});
}
+fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
+ const prefetch = f.air.instructions.items(.data)[inst].prefetch;
+ switch (prefetch.cache) {
+ .data => {},
+ // The available prefetch intrinsics do not accept a cache argument; only
+ // address, rw, and locality. So unless the cache is data, we do not lower
+ // this instruction.
+ .instruction => return CValue.none,
+ }
+ const ptr = try f.resolveInst(prefetch.ptr);
+ const writer = f.object.writer();
+ try writer.writeAll("zig_prefetch(");
+ try f.writeCValue(writer, ptr);
+ try writer.print(", {d}, {d});\n", .{
+ @enumToInt(prefetch.rw), prefetch.locality,
+ });
+ return CValue.none;
+}
+
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
return switch (order) {
.Unordered => "memory_order_relaxed",
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index d8c9589213..7a496af7b1 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -2073,6 +2073,7 @@ pub const FuncGen = struct {
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
.vector_init => try self.airVectorInit(inst),
+ .prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -4384,6 +4385,67 @@ pub const FuncGen = struct {
return vector;
}
+ fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+
+ comptime assert(@enumToInt(std.builtin.PrefetchOptions.Rw.read) == 0);
+ comptime assert(@enumToInt(std.builtin.PrefetchOptions.Rw.write) == 1);
+
+ // TODO these two asserts should be able to be comptime because the type is a u2
+ assert(prefetch.locality >= 0);
+ assert(prefetch.locality <= 3);
+
+ comptime assert(@enumToInt(std.builtin.PrefetchOptions.Cache.instruction) == 0);
+ comptime assert(@enumToInt(std.builtin.PrefetchOptions.Cache.data) == 1);
+
+ // LLVM fails during codegen of instruction cache prefetchs for these architectures.
+ // This is an LLVM bug as the prefetch intrinsic should be a noop if not supported
+ // by the target.
+ // To work around this, don't emit llvm.prefetch in this case.
+ // See https://bugs.llvm.org/show_bug.cgi?id=21037
+ const target = self.dg.module.getTarget();
+ switch (prefetch.cache) {
+ .instruction => switch (target.cpu.arch) {
+ .x86_64, .i386 => return null,
+ .arm, .armeb, .thumb, .thumbeb => {
+ switch (prefetch.rw) {
+ .write => return null,
+ else => {},
+ }
+ },
+ else => {},
+ },
+ .data => {},
+ }
+
+ const llvm_u8 = self.context.intType(8);
+ const llvm_ptr_u8 = llvm_u8.pointerType(0);
+ const llvm_u32 = self.context.intType(32);
+
+ const llvm_fn_name = "llvm.prefetch.p0i8";
+ const fn_val = self.dg.object.llvm_module.getNamedFunction(llvm_fn_name) orelse blk: {
+ // declare void @llvm.prefetch(i8*, i32, i32, i32)
+ const llvm_void = self.context.voidType();
+ const param_types = [_]*const llvm.Type{
+ llvm_ptr_u8, llvm_u32, llvm_u32, llvm_u32,
+ };
+ const fn_type = llvm.functionType(llvm_void, &param_types, param_types.len, .False);
+ break :blk self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type);
+ };
+
+ const ptr = try self.resolveInst(prefetch.ptr);
+ const ptr_u8 = self.builder.buildBitCast(ptr, llvm_ptr_u8, "");
+
+ const params = [_]*const llvm.Value{
+ ptr_u8,
+ llvm_u32.constInt(@enumToInt(prefetch.rw), .False),
+ llvm_u32.constInt(prefetch.locality, .False),
+ llvm_u32.constInt(@enumToInt(prefetch.cache), .False),
+ };
+ _ = self.builder.buildCall(fn_val, &params, params.len, .C, .Auto, "");
+ return null;
+ }
+
fn getErrorNameTable(self: *FuncGen) !*const llvm.Value {
if (self.dg.object.error_name_table) |table| {
return table;
diff --git a/src/link/C/zig.h b/src/link/C/zig.h
index b14aaaaf34..eeda93894b 100644
--- a/src/link/C/zig.h
+++ b/src/link/C/zig.h
@@ -74,6 +74,12 @@
#define zig_frame_address() 0
#endif
+#if defined(__GNUC__)
+#define zig_prefetch(addr, rw, locality) __builtin_prefetch(addr, rw, locality)
+#else
+#define zig_prefetch(addr, rw, locality)
+#endif
+
#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__)
#include <stdatomic.h>
#define zig_cmpxchg_strong(obj, expected, desired, succ, fail) atomic_compare_exchange_strong_explicit(obj, &(expected), desired, succ, fail)
diff --git a/src/print_air.zig b/src/print_air.zig
index 10ca034d1f..4e0b7cd4fd 100644
--- a/src/print_air.zig
+++ b/src/print_air.zig
@@ -226,6 +226,7 @@ const Writer = struct {
.cmpxchg_weak, .cmpxchg_strong => try w.writeCmpxchg(s, inst),
.fence => try w.writeFence(s, inst),
.atomic_load => try w.writeAtomicLoad(s, inst),
+ .prefetch => try w.writePrefetch(s, inst),
.atomic_store_unordered => try w.writeAtomicStore(s, inst, .Unordered),
.atomic_store_monotonic => try w.writeAtomicStore(s, inst, .Monotonic),
.atomic_store_release => try w.writeAtomicStore(s, inst, .Release),
@@ -350,6 +351,15 @@ const Writer = struct {
try s.print(", {s}", .{@tagName(atomic_load.order)});
}
+ fn writePrefetch(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+ const prefetch = w.air.instructions.items(.data)[inst].prefetch;
+
+ try w.writeOperand(s, inst, 0, prefetch.ptr);
+ try s.print(", {s}, {d}, {s}", .{
+ @tagName(prefetch.rw), prefetch.locality, @tagName(prefetch.cache),
+ });
+ }
+
fn writeAtomicStore(
w: *Writer,
s: anytype,
diff --git a/test/behavior.zig b/test/behavior.zig
index 25db4919ae..fe73529810 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -10,6 +10,7 @@ test {
_ = @import("behavior/fn_in_struct_in_comptime.zig");
_ = @import("behavior/hasdecl.zig");
_ = @import("behavior/hasfield.zig");
+ _ = @import("behavior/prefetch.zig");
_ = @import("behavior/pub_enum.zig");
_ = @import("behavior/type_info.zig");
_ = @import("behavior/type.zig");
@@ -178,7 +179,6 @@ test {
_ = @import("behavior/optional_stage1.zig");
_ = @import("behavior/pointers_stage1.zig");
_ = @import("behavior/popcount_stage1.zig");
- _ = @import("behavior/prefetch.zig");
_ = @import("behavior/ptrcast_stage1.zig");
_ = @import("behavior/reflection.zig");
_ = @import("behavior/saturating_arithmetic_stage1.zig");