Skip to content

Commit 97c0373

Browse files
authored
Merge pull request #10295 from ifreund/prefetch
Implement @prefetch()
2 parents 75f3e7a + 516945d commit 97c0373

16 files changed

+395
-7
lines changed

doc/langref.html.in

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8674,6 +8674,50 @@ test "@wasmMemoryGrow" {
86748674
{#see_also|@ctz|@clz#}
86758675
{#header_close#}
86768676

8677+
{#header_open|@prefetch#}
8678+
<pre>{#syntax#}@prefetch(ptr: anytype, comptime options: std.builtin.PrefetchOptions){#endsyntax#}</pre>
8679+
<p>
8680+
This builtin tells the compiler to emit a prefetch instruction if supported by the
8681+
target CPU. If the target CPU does not support the requested prefetch instruction,
8682+
this builtin is a noop. This function has no effect on the behavior of the program,
8683+
only on the performance characteristics.
8684+
</p>
8685+
<p>
8686+
The {#syntax#}ptr{#endsyntax#} argument may be any pointer type and determines the memory
8687+
address to prefetch. This function does not dereference the pointer, it is perfectly legal
8688+
to pass a pointer to invalid memory to this function and no illegal behavior will result.
8689+
</p>
8690+
<p>
8691+
The {#syntax#}options{#endsyntax#} argument is the following struct:
8692+
</p>
8693+
{#code_begin|syntax|builtin#}
8694+
/// This data structure is used by the Zig language code generation and
8695+
/// therefore must be kept in sync with the compiler implementation.
8696+
pub const PrefetchOptions = struct {
8697+
/// Whether the prefetch should prepare for a read or a write.
8698+
rw: Rw = .read,
8699+
/// 0 means no temporal locality. That is, the data can be immediately
8700+
/// dropped from the cache after it is accessed.
8701+
///
8702+
/// 3 means high temporal locality. That is, the data should be kept in
8703+
/// the cache as it is likely to be accessed again soon.
8704+
locality: u2 = 3,
8705+
/// The cache that the prefetch should be preformed on.
8706+
cache: Cache = .data,
8707+
8708+
pub const Rw = enum {
8709+
read,
8710+
write,
8711+
};
8712+
8713+
pub const Cache = enum {
8714+
instruction,
8715+
data,
8716+
};
8717+
};
8718+
{#code_end#}
8719+
{#header_close#}
8720+
86778721
{#header_open|@ptrCast#}
86788722
<pre>{#syntax#}@ptrCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
86798723
<p>

lib/std/builtin.zig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,31 @@ pub const CallOptions = struct {
651651
};
652652
};
653653

654+
/// This data structure is used by the Zig language code generation and
655+
/// therefore must be kept in sync with the compiler implementation.
656+
pub const PrefetchOptions = struct {
657+
/// Whether the prefetch should prepare for a read or a write.
658+
rw: Rw = .read,
659+
/// 0 means no temporal locality. That is, the data can be immediately
660+
/// dropped from the cache after it is accessed.
661+
///
662+
/// 3 means high temporal locality. That is, the data should be kept in
663+
/// the cache as it is likely to be accessed again soon.
664+
locality: u2 = 3,
665+
/// The cache that the prefetch should be preformed on.
666+
cache: Cache = .data,
667+
668+
pub const Rw = enum {
669+
read,
670+
write,
671+
};
672+
673+
pub const Cache = enum {
674+
instruction,
675+
data,
676+
};
677+
};
678+
654679
/// This data structure is used by the Zig language code generation and
655680
/// therefore must be kept in sync with the compiler implementation.
656681
pub const ExportOptions = struct {

src/AstGen.zig

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6739,7 +6739,6 @@ fn builtinCall(
67396739
}
67406740
}
67416741

6742-
// zig fmt: off
67436742
switch (info.tag) {
67446743
.import => {
67456744
const node_tags = tree.nodes.items(.tag);
@@ -6768,7 +6767,7 @@ fn builtinCall(
67686767
astgen.extra.items[extra_index] = @enumToInt(param_ref);
67696768
extra_index += 1;
67706769
}
6771-
const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log,payload_index, params.len);
6770+
const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
67726771
return rvalue(gz, rl, result, node);
67736772
},
67746773
.field => {
@@ -6784,11 +6783,14 @@ fn builtinCall(
67846783
});
67856784
return rvalue(gz, rl, result, node);
67866785
},
6786+
6787+
// zig fmt: off
67876788
.as => return as( gz, scope, rl, node, params[0], params[1]),
67886789
.bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),
67896790
.TypeOf => return typeOf( gz, scope, rl, node, params),
67906791
.union_init => return unionInit(gz, scope, rl, node, params),
67916792
.c_import => return cImport( gz, scope, node, params[0]),
6793+
// zig fmt: on
67926794

67936795
.@"export" => {
67946796
const node_tags = tree.nodes.items(.tag);
@@ -6858,9 +6860,7 @@ fn builtinCall(
68586860
const field_ident = dot_token + 1;
68596861
decl_name = try astgen.identAsString(field_ident);
68606862
},
6861-
else => return astgen.failNode(
6862-
params[0], "symbol to export must identify a declaration", .{},
6863-
),
6863+
else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
68646864
}
68656865
const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]);
68666866
_ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
@@ -6888,6 +6888,7 @@ fn builtinCall(
68886888

68896889
.breakpoint => return simpleNoOpVoid(gz, rl, node, .breakpoint),
68906890

6891+
// zig fmt: off
68916892
.This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),
68926893
.return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),
68936894
.src => return rvalue(gz, rl, try gz.addNodeExtended(.builtin_src, node), node),
@@ -6942,6 +6943,8 @@ fn builtinCall(
69426943
.err_set_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .err_set_cast),
69436944
.ptr_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .ptr_cast),
69446945
.truncate => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate),
6946+
// zig fmt: on
6947+
69456948
.align_cast => {
69466949
const dest_align = try comptimeExpr(gz, scope, align_rl, params[0]);
69476950
const rhs = try expr(gz, scope, .none, params[1]);
@@ -6952,6 +6955,7 @@ fn builtinCall(
69526955
return rvalue(gz, rl, result, node);
69536956
},
69546957

6958+
// zig fmt: off
69556959
.has_decl => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_decl),
69566960
.has_field => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_field),
69576961

@@ -6978,6 +6982,7 @@ fn builtinCall(
69786982

69796983
.cmpxchg_strong => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_strong),
69806984
.cmpxchg_weak => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_weak),
6985+
// zig fmt: on
69816986

69826987
.wasm_memory_size => {
69836988
const operand = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
@@ -7221,8 +7226,17 @@ fn builtinCall(
72217226
});
72227227
return rvalue(gz, rl, result, node);
72237228
},
7229+
.prefetch => {
7230+
const ptr = try expr(gz, scope, .none, params[0]);
7231+
const options = try comptimeExpr(gz, scope, .{ .ty = .prefetch_options_type }, params[1]);
7232+
const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
7233+
.node = gz.nodeIndexToRelative(node),
7234+
.lhs = ptr,
7235+
.rhs = options,
7236+
});
7237+
return rvalue(gz, rl, result, node);
7238+
},
72247239
}
7225-
// zig fmt: on
72267240
}
72277241

72287242
fn simpleNoOpVoid(

src/BuiltinFn.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub const Tag = enum {
6767
mul_with_overflow,
6868
panic,
6969
pop_count,
70+
prefetch,
7071
ptr_cast,
7172
ptr_to_int,
7273
rem,
@@ -615,6 +616,13 @@ pub const list = list: {
615616
.param_count = 2,
616617
},
617618
},
619+
.{
620+
"@prefetch",
621+
.{
622+
.tag = .prefetch,
623+
.param_count = 2,
624+
},
625+
},
618626
.{
619627
"@ptrCast",
620628
.{

src/Sema.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
10421042
.c_define => return sema.zirCDefine( block, extended),
10431043
.wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
10441044
.wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
1045+
.prefetch => return sema.zirPrefetch( block, extended),
10451046
// zig fmt: on
10461047
}
10471048
}
@@ -11104,6 +11105,16 @@ fn zirWasmMemoryGrow(
1110411105
return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
1110511106
}
1110611107

11108+
fn zirPrefetch(
11109+
sema: *Sema,
11110+
block: *Block,
11111+
extended: Zir.Inst.Extended.InstData,
11112+
) CompileError!Air.Inst.Ref {
11113+
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
11114+
const src: LazySrcLoc = .{ .node_offset = extra.node };
11115+
return sema.fail(block, src, "TODO: implement Sema.zirPrefetch", .{});
11116+
}
11117+
1110711118
fn zirBuiltinExtern(
1110811119
sema: *Sema,
1110911120
block: *Block,
@@ -14231,6 +14242,7 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
1423114242
.float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
1423214243
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
1423314244
.call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
14245+
.prefetch_options => return sema.resolveBuiltinTypeFields(block, src, "PrefetchOptions"),
1423414246

1423514247
.@"union", .union_tagged => {
1423614248
const union_obj = ty.cast(Type.Payload.Union).?.data;
@@ -14819,6 +14831,7 @@ fn typeHasOnePossibleValue(
1481914831
.float_mode,
1482014832
.reduce_op,
1482114833
.call_options,
14834+
.prefetch_options,
1482214835
.export_options,
1482314836
.extern_options,
1482414837
.type_info,
@@ -15032,6 +15045,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
1503215045
.float_mode => return .float_mode_type,
1503315046
.reduce_op => return .reduce_op_type,
1503415047
.call_options => return .call_options_type,
15048+
.prefetch_options => return .prefetch_options_type,
1503515049
.export_options => return .export_options_type,
1503615050
.extern_options => return .extern_options_type,
1503715051
.type_info => return .type_info_type,

src/Zir.zig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,6 +1572,9 @@ pub const Inst = struct {
15721572
wasm_memory_size,
15731573
/// `operand` is payload index to `BinNode`.
15741574
wasm_memory_grow,
1575+
/// The `@prefetch` builtin.
1576+
/// `operand` is payload index to `BinNode`.
1577+
prefetch,
15751578

15761579
pub const InstData = struct {
15771580
opcode: Extended,
@@ -1648,6 +1651,7 @@ pub const Inst = struct {
16481651
float_mode_type,
16491652
reduce_op_type,
16501653
call_options_type,
1654+
prefetch_options_type,
16511655
export_options_type,
16521656
extern_options_type,
16531657
type_info_type,
@@ -1917,6 +1921,10 @@ pub const Inst = struct {
19171921
.ty = Type.initTag(.type),
19181922
.val = Value.initTag(.call_options_type),
19191923
},
1924+
.prefetch_options_type = .{
1925+
.ty = Type.initTag(.type),
1926+
.val = Value.initTag(.prefetch_options_type),
1927+
},
19201928
.export_options_type = .{
19211929
.ty = Type.initTag(.type),
19221930
.val = Value.initTag(.export_options_type),

src/print_zir.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ const Writer = struct {
477477
try self.writeSrc(stream, src);
478478
},
479479

480-
.builtin_extern, .c_define, .wasm_memory_grow => {
480+
.builtin_extern, .c_define, .wasm_memory_grow, .prefetch => {
481481
const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
482482
const src: LazySrcLoc = .{ .node_offset = inst_data.node };
483483
try self.writeInstRef(stream, inst_data.lhs);

src/stage1/all_types.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,18 @@ struct AstNodeFnCallExpr {
898898
bool seen; // used by @compileLog
899899
};
900900

901+
// Must be kept in sync with std.builtin.PrefetchOptions.Rw
902+
enum PrefetchRw {
903+
PrefetchRwRead,
904+
PrefetchRwWrite,
905+
};
906+
907+
// Must be kept in sync with std.builtin.PrefetchOptions.Cache
908+
enum PrefetchCache {
909+
PrefetchCacheInstruction,
910+
PrefetchCacheData,
911+
};
912+
901913
struct AstNodeArrayAccessExpr {
902914
AstNode *array_ref_expr;
903915
AstNode *subscript;
@@ -1818,6 +1830,7 @@ enum BuiltinFnId {
18181830
BuiltinFnIdReduce,
18191831
BuiltinFnIdMaximum,
18201832
BuiltinFnIdMinimum,
1833+
BuiltinFnIdPrefetch,
18211834
};
18221835

18231836
struct BuiltinFnEntry {
@@ -2021,6 +2034,7 @@ struct CodeGen {
20212034
LLVMValueRef return_err_fn;
20222035
LLVMValueRef wasm_memory_size;
20232036
LLVMValueRef wasm_memory_grow;
2037+
LLVMValueRef prefetch;
20242038
LLVMTypeRef anyframe_fn_type;
20252039

20262040
// reminder: hash tables must be initialized before use
@@ -2647,6 +2661,7 @@ enum Stage1ZirInstId : uint8_t {
26472661
Stage1ZirInstIdWasmMemorySize,
26482662
Stage1ZirInstIdWasmMemoryGrow,
26492663
Stage1ZirInstIdSrc,
2664+
Stage1ZirInstIdPrefetch,
26502665
};
26512666

26522667
// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
@@ -2743,6 +2758,7 @@ enum Stage1AirInstId : uint8_t {
27432758
Stage1AirInstIdWasmMemorySize,
27442759
Stage1AirInstIdWasmMemoryGrow,
27452760
Stage1AirInstIdExtern,
2761+
Stage1AirInstIdPrefetch,
27462762
};
27472763

27482764
struct Stage1ZirInst {
@@ -3683,6 +3699,24 @@ struct Stage1ZirInstSrc {
36833699
Stage1ZirInst base;
36843700
};
36853701

3702+
struct Stage1ZirInstPrefetch {
3703+
Stage1ZirInst base;
3704+
3705+
Stage1ZirInst *ptr;
3706+
Stage1ZirInst *options;
3707+
};
3708+
3709+
struct Stage1AirInstPrefetch {
3710+
Stage1AirInst base;
3711+
3712+
Stage1AirInst *ptr;
3713+
PrefetchRw rw;
3714+
// Must be in the range 0-3 inclusive
3715+
uint8_t locality;
3716+
PrefetchCache cache;
3717+
};
3718+
3719+
36863720
struct Stage1ZirInstSlice {
36873721
Stage1ZirInst base;
36883722

0 commit comments

Comments
 (0)