authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-10 18:54:56-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-12-10 18:54:56-08:00
log97c0373fa7ee125d2d4e73f9b1dc8ea0e2c2e86a
treed2aa342a53aef95f88456a63fa7874f3c51ba3b8
parent75f3e7a4a05db7ea805e581f78117a41768945ae
parent516945d7d9b4d8b969d037076799d4ed29cecdda
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10295 from ifreund/prefetch

Implement @prefetch()

16 files changed, 395 insertions(+), 7 deletions(-)

doc/langref.html.in+44
......@@ -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.
8696pub 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+25
......@@ -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.
656pub 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.
656681pub const ExportOptions = struct {
src/AstGen.zig+20-6
......@@ -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
72287242fn simpleNoOpVoid(
src/BuiltinFn.zig+8
......@@ -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+14
......@@ -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
11108fn 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
1110711118fn 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+8
......@@ -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+1-1
......@@ -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+34
......@@ -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
902enum PrefetchRw {
903 PrefetchRwRead,
904 PrefetchRwWrite,
905};
906
907// Must be kept in sync with std.builtin.PrefetchOptions.Cache
908enum PrefetchCache {
909 PrefetchCacheInstruction,
910 PrefetchCacheData,
911};
912
901913struct 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
18231836struct 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
27482764struct Stage1ZirInst {
......@@ -3683,6 +3699,24 @@ struct Stage1ZirInstSrc {
36833699 Stage1ZirInst base;
36843700};
36853701
3702struct Stage1ZirInstPrefetch {
3703 Stage1ZirInst base;
3704
3705 Stage1ZirInst *ptr;
3706 Stage1ZirInst *options;
3707};
3708
3709struct 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
36863720struct Stage1ZirInstSlice {
36873721 Stage1ZirInst base;
36883722
src/stage1/astgen.cpp+44
......@@ -349,6 +349,8 @@ void destroy_instruction_src(Stage1ZirInst *inst) {
349349 return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstWasmMemoryGrow *>(inst));
350350 case Stage1ZirInstIdSrc:
351351 return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstSrc *>(inst));
352 case Stage1ZirInstIdPrefetch:
353 return heap::c_allocator.destroy(reinterpret_cast<Stage1ZirInstPrefetch *>(inst));
352354 }
353355 zig_unreachable();
354356}
......@@ -941,6 +943,10 @@ static constexpr Stage1ZirInstId ir_inst_id(Stage1ZirInstSrc *) {
941943 return Stage1ZirInstIdSrc;
942944}
943945
946static constexpr Stage1ZirInstId ir_inst_id(Stage1ZirInstPrefetch *) {
947 return Stage1ZirInstIdPrefetch;
948}
949
944950template<typename T>
945951static T *ir_create_instruction(Stage1AstGen *ag, Scope *scope, AstNode *source_node) {
946952 T *special_instruction = heap::c_allocator.create<T>();
......@@ -2870,6 +2876,21 @@ static Stage1ZirInst *ir_build_src(Stage1AstGen *ag, Scope *scope, AstNode *sour
28702876 return &instruction->base;
28712877}
28722878
2879static Stage1ZirInst *ir_build_prefetch(Stage1AstGen *ag, Scope *scope, AstNode *source_node,
2880 Stage1ZirInst *ptr, Stage1ZirInst *options)
2881{
2882 Stage1ZirInstPrefetch *prefetch_instruction = ir_build_instruction<Stage1ZirInstPrefetch>(
2883 ag, scope, source_node);
2884 prefetch_instruction->ptr = ptr;
2885 prefetch_instruction->options = options;
2886
2887 ir_ref_instruction(ptr, ag->current_basic_block);
2888 ir_ref_instruction(options, ag->current_basic_block);
2889
2890 return &prefetch_instruction->base;
2891}
2892
2893
28732894static void ir_count_defers(Stage1AstGen *ag, Scope *inner_scope, Scope *outer_scope, size_t *results) {
28742895 results[ReturnKindUnconditional] = 0;
28752896 results[ReturnKindError] = 0;
......@@ -5416,6 +5437,29 @@ static Stage1ZirInst *astgen_builtin_fn_call(Stage1AstGen *ag, Scope *scope, Ast
54165437 Stage1ZirInst *src_inst = ir_build_src(ag, scope, node);
54175438 return ir_lval_wrap(ag, scope, src_inst, lval, result_loc);
54185439 }
5440 case BuiltinFnIdPrefetch:
5441 {
5442 ZigType *options_type = get_builtin_type(ag->codegen, "PrefetchOptions");
5443 Stage1ZirInst *options_type_inst = ir_build_const_type(ag, scope, node, options_type);
5444 ResultLocCast *result_loc_cast = ir_build_cast_result_loc(ag, options_type_inst, no_result_loc());
5445
5446 AstNode *ptr_node = node->data.fn_call_expr.params.at(0);
5447 Stage1ZirInst *ptr_value = astgen_node(ag, ptr_node, scope);
5448 if (ptr_value == ag->codegen->invalid_inst_src)
5449 return ptr_value;
5450
5451 AstNode *options_node = node->data.fn_call_expr.params.at(1);
5452 Stage1ZirInst *options_value = astgen_node_extra(ag, options_node,
5453 scope, LValNone, &result_loc_cast->base);
5454 if (options_value == ag->codegen->invalid_inst_src)
5455 return options_value;
5456
5457 Stage1ZirInst *casted_options_value = ir_build_implicit_cast(
5458 ag, scope, options_node, options_value, result_loc_cast);
5459
5460 Stage1ZirInst *ir_extern = ir_build_prefetch(ag, scope, node, ptr_value, casted_options_value);
5461 return ir_lval_wrap(ag, scope, ir_extern, lval, result_loc);
5462 }
54195463 }
54205464 zig_unreachable();
54215465}
src/stage1/codegen.cpp+67
......@@ -1139,6 +1139,24 @@ static LLVMValueRef gen_wasm_memory_grow(CodeGen *g) {
11391139 return g->wasm_memory_grow;
11401140}
11411141
1142static LLVMValueRef gen_prefetch(CodeGen *g) {
1143 if (g->prefetch)
1144 return g->prefetch;
1145
1146 // declare void @llvm.prefetch(i8*, i32, i32, i32)
1147 LLVMTypeRef param_types[] = {
1148 LLVMPointerType(LLVMInt8Type(), 0),
1149 LLVMInt32Type(),
1150 LLVMInt32Type(),
1151 LLVMInt32Type(),
1152 };
1153 LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 4, false);
1154 g->prefetch = LLVMAddFunction(g->module, "llvm.prefetch.p0i8", fn_type);
1155 assert(LLVMGetIntrinsicID(g->prefetch));
1156
1157 return g->prefetch;
1158}
1159
11421160static LLVMValueRef get_stacksave_fn_val(CodeGen *g) {
11431161 if (g->stacksave_fn_val)
11441162 return g->stacksave_fn_val;
......@@ -5899,6 +5917,52 @@ static LLVMValueRef ir_render_wasm_memory_grow(CodeGen *g, Stage1Air *executable
58995917 return val;
59005918}
59015919
5920static LLVMValueRef ir_render_prefetch(CodeGen *g, Stage1Air *executable, Stage1AirInstPrefetch *instruction) {
5921 static_assert(PrefetchRwRead == 0, "");
5922 static_assert(PrefetchRwWrite == 1, "");
5923 assert(instruction->rw == PrefetchRwRead || instruction->rw == PrefetchRwWrite);
5924
5925 assert(instruction->locality >= 0 && instruction->locality <= 3);
5926
5927 static_assert(PrefetchCacheInstruction == 0, "");
5928 static_assert(PrefetchCacheData == 1, "");
5929 assert(instruction->cache == PrefetchCacheData || instruction->cache == PrefetchCacheInstruction);
5930
5931 // LLVM fails during codegen of instruction cache prefetchs for these architectures.
5932 // This is an LLVM bug as the prefetch intrinsic should be a noop if not supported by the target.
5933 // To work around this, simply don't emit llvm.prefetch in this case.
5934 // See https://bugs.llvm.org/show_bug.cgi?id=21037
5935 if (instruction->cache == PrefetchCacheInstruction) {
5936 switch (g->zig_target->arch) {
5937 case ZigLLVM_x86:
5938 case ZigLLVM_x86_64:
5939 return nullptr;
5940 default:
5941 break;
5942 }
5943 }
5944
5945 // Another case of the same LLVM bug described above
5946 if (instruction->rw == PrefetchRwWrite && instruction->cache == PrefetchCacheInstruction) {
5947 switch (g->zig_target->arch) {
5948 case ZigLLVM_arm:
5949 return nullptr;
5950 default:
5951 break;
5952 }
5953
5954 }
5955
5956 LLVMValueRef params[] = {
5957 LLVMBuildBitCast(g->builder, ir_llvm_value(g, instruction->ptr), LLVMPointerType(LLVMInt8Type(), 0), ""),
5958 LLVMConstInt(LLVMInt32Type(), instruction->rw, false),
5959 LLVMConstInt(LLVMInt32Type(), instruction->locality, false),
5960 LLVMConstInt(LLVMInt32Type(), instruction->cache, false),
5961 };
5962 LLVMValueRef val = LLVMBuildCall(g->builder, gen_prefetch(g), params, 4, "");
5963 return val;
5964}
5965
59025966static LLVMValueRef ir_render_slice(CodeGen *g, Stage1Air *executable, Stage1AirInstSlice *instruction) {
59035967 Error err;
59045968
......@@ -7150,6 +7214,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, Stage1Air *executable, Sta
71507214 return ir_render_wasm_memory_grow(g, executable, (Stage1AirInstWasmMemoryGrow *) instruction);
71517215 case Stage1AirInstIdExtern:
71527216 return ir_render_extern(g, executable, (Stage1AirInstExtern *) instruction);
7217 case Stage1AirInstIdPrefetch:
7218 return ir_render_prefetch(g, executable, (Stage1AirInstPrefetch *) instruction);
71537219 }
71547220 zig_unreachable();
71557221}
......@@ -9120,6 +9186,7 @@ static void define_builtin_fns(CodeGen *g) {
91209186 create_builtin_fn(g, BuiltinFnIdReduce, "reduce", 2);
91219187 create_builtin_fn(g, BuiltinFnIdMaximum, "maximum", 2);
91229188 create_builtin_fn(g, BuiltinFnIdMinimum, "minimum", 2);
9189 create_builtin_fn(g, BuiltinFnIdPrefetch, "prefetch", 2);
91239190}
91249191
91259192static const char *bool_to_str(bool b) {
src/stage1/ir.cpp+56
......@@ -467,6 +467,8 @@ void destroy_instruction_gen(Stage1AirInst *inst) {
467467 return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstWasmMemoryGrow *>(inst));
468468 case Stage1AirInstIdExtern:
469469 return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstExtern *>(inst));
470 case Stage1AirInstIdPrefetch:
471 return heap::c_allocator.destroy(reinterpret_cast<Stage1AirInstPrefetch *>(inst));
470472 }
471473 zig_unreachable();
472474}
......@@ -1115,6 +1117,10 @@ static constexpr Stage1AirInstId ir_inst_id(Stage1AirInstExtern *) {
11151117 return Stage1AirInstIdExtern;
11161118}
11171119
1120static constexpr Stage1AirInstId ir_inst_id(Stage1AirInstPrefetch *) {
1121 return Stage1AirInstIdPrefetch;
1122}
1123
11181124template<typename T>
11191125static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) {
11201126 T *special_instruction = heap::c_allocator.create<T>();
......@@ -24853,6 +24859,52 @@ static Stage1AirInst *ir_analyze_instruction_src(IrAnalyze *ira, Stage1ZirInstSr
2485324859 return ir_const_move(ira, instruction->base.scope, instruction->base.source_node, result);
2485424860}
2485524861
24862static Stage1AirInst *ir_analyze_instruction_prefetch(IrAnalyze *ira, Stage1ZirInstPrefetch *instruction) {
24863 Stage1AirInst *ptr = instruction->ptr->child;
24864 if (type_is_invalid(ptr->value->type))
24865 return ira->codegen->invalid_inst_gen;
24866
24867 Stage1AirInst *raw_options_inst = instruction->options->child;
24868 if (type_is_invalid(raw_options_inst->value->type))
24869 return ira->codegen->invalid_inst_gen;
24870
24871 ZigType *options_type = get_builtin_type(ira->codegen, "PrefetchOptions");
24872 Stage1AirInst *options_inst = ir_implicit_cast(ira, raw_options_inst, options_type);
24873 if (type_is_invalid(options_inst->value->type))
24874 return ira->codegen->invalid_inst_gen;
24875
24876 ZigValue *options_val = ir_resolve_const(ira, options_inst, UndefBad);
24877 if (options_val == nullptr)
24878 return ira->codegen->invalid_inst_gen;
24879
24880 ZigValue *rw_val = get_const_field(ira, options_inst->source_node, options_val, "rw", 0);
24881 if (rw_val == nullptr)
24882 return ira->codegen->invalid_inst_gen;
24883 PrefetchRw rw = (PrefetchRw)bigint_as_u8(&rw_val->data.x_enum_tag);
24884
24885 ZigValue *locality_val = get_const_field(ira, options_inst->source_node, options_val, "locality", 1);
24886 if (locality_val == nullptr)
24887 return ira->codegen->invalid_inst_gen;
24888 uint8_t locality = bigint_as_u8(&locality_val->data.x_bigint);
24889 assert(locality <= 3);
24890
24891 ZigValue *cache_val = get_const_field(ira, options_inst->source_node, options_val, "cache", 2);
24892 if (cache_val == nullptr)
24893 return ira->codegen->invalid_inst_gen;
24894 PrefetchCache cache = (PrefetchCache)bigint_as_u8(&cache_val->data.x_enum_tag);
24895
24896 Stage1AirInstPrefetch *air_instruction = ir_build_inst_void<Stage1AirInstPrefetch>(&ira->new_irb,
24897 instruction->base.scope, instruction->base.source_node);
24898 air_instruction->ptr = ptr;
24899 air_instruction->rw = rw;
24900 air_instruction->locality = locality;
24901 air_instruction->cache = cache;
24902
24903 ir_ref_inst_gen(ptr);
24904
24905 return &air_instruction->base;
24906}
24907
2485624908static Stage1AirInst *ir_analyze_instruction_base(IrAnalyze *ira, Stage1ZirInst *instruction) {
2485724909 switch (instruction->id) {
2485824910 case Stage1ZirInstIdInvalid:
......@@ -25138,6 +25190,8 @@ static Stage1AirInst *ir_analyze_instruction_base(IrAnalyze *ira, Stage1ZirInst
2513825190 return ir_analyze_instruction_wasm_memory_grow(ira, (Stage1ZirInstWasmMemoryGrow *)instruction);
2513925191 case Stage1ZirInstIdSrc:
2514025192 return ir_analyze_instruction_src(ira, (Stage1ZirInstSrc *)instruction);
25193 case Stage1ZirInstIdPrefetch:
25194 return ir_analyze_instruction_prefetch(ira, (Stage1ZirInstPrefetch *)instruction);
2514125195 }
2514225196 zig_unreachable();
2514325197}
......@@ -25305,6 +25359,7 @@ bool ir_inst_gen_has_side_effects(Stage1AirInst *instruction) {
2530525359 case Stage1AirInstIdSpillBegin:
2530625360 case Stage1AirInstIdWasmMemoryGrow:
2530725361 case Stage1AirInstIdExtern:
25362 case Stage1AirInstIdPrefetch:
2530825363 return true;
2530925364
2531025365 case Stage1AirInstIdPhi:
......@@ -25444,6 +25499,7 @@ bool ir_inst_src_has_side_effects(Stage1ZirInst *instruction) {
2544425499 case Stage1ZirInstIdAwait:
2544525500 case Stage1ZirInstIdSpillBegin:
2544625501 case Stage1ZirInstIdWasmMemoryGrow:
25502 case Stage1ZirInstIdPrefetch:
2544725503 return true;
2544825504
2544925505 case Stage1ZirInstIdPhi:
src/stage1/ir_print.cpp+22
......@@ -371,6 +371,8 @@ const char* ir_inst_src_type_str(Stage1ZirInstId id) {
371371 return "SrcWasmMemoryGrow";
372372 case Stage1ZirInstIdSrc:
373373 return "SrcSrc";
374 case Stage1ZirInstIdPrefetch:
375 return "SrcPrefetch";
374376 }
375377 zig_unreachable();
376378}
......@@ -559,6 +561,8 @@ const char* ir_inst_gen_type_str(Stage1AirInstId id) {
559561 return "GenWasmMemoryGrow";
560562 case Stage1AirInstIdExtern:
561563 return "GenExtern";
564 case Stage1AirInstIdPrefetch:
565 return "GenPrefetch";
562566 }
563567 zig_unreachable();
564568}
......@@ -2436,6 +2440,18 @@ static void ir_print_extern(IrPrintSrc *irp, Stage1ZirInstExtern *instruction) {
24362440 fprintf(irp->f, ")");
24372441}
24382442
2443static void ir_print_prefetch(IrPrintSrc *irp, Stage1ZirInstPrefetch *instruction) {
2444 fprintf(irp->f, "@prefetch(");
2445 ir_print_other_inst_src(irp, instruction->ptr);
2446 fprintf(irp->f, ",");
2447 ir_print_other_inst_src(irp, instruction->options);
2448 fprintf(irp->f, ")");
2449}
2450
2451static void ir_print_prefetch(IrPrintGen *irp, Stage1AirInstPrefetch *instruction) {
2452 fprintf(irp->f, "@prefetch(...)");
2453}
2454
24392455static void ir_print_error_return_trace(IrPrintSrc *irp, Stage1ZirInstErrorReturnTrace *instruction) {
24402456 fprintf(irp->f, "@errorReturnTrace(");
24412457 switch (instruction->optional) {
......@@ -3108,6 +3124,9 @@ static void ir_print_inst_src(IrPrintSrc *irp, Stage1ZirInst *instruction, bool
31083124 case Stage1ZirInstIdSrc:
31093125 ir_print_builtin_src(irp, (Stage1ZirInstSrc *)instruction);
31103126 break;
3127 case Stage1ZirInstIdPrefetch:
3128 ir_print_prefetch(irp, (Stage1ZirInstPrefetch *)instruction);
3129 break;
31113130 }
31123131 fprintf(irp->f, "\n");
31133132}
......@@ -3387,6 +3406,9 @@ static void ir_print_inst_gen(IrPrintGen *irp, Stage1AirInst *instruction, bool
33873406 case Stage1AirInstIdExtern:
33883407 ir_print_extern(irp, (Stage1AirInstExtern *)instruction);
33893408 break;
3409 case Stage1AirInstIdPrefetch:
3410 ir_print_prefetch(irp, (Stage1AirInstPrefetch *)instruction);
3411 break;
33903412
33913413 }
33923414 fprintf(irp->f, "\n");
src/type.zig+19
......@@ -123,6 +123,7 @@ pub const Type = extern union {
123123 .empty_struct_literal,
124124 .@"struct",
125125 .call_options,
126 .prefetch_options,
126127 .export_options,
127128 .extern_options,
128129 => return .Struct,
......@@ -798,6 +799,7 @@ pub const Type = extern union {
798799 .float_mode,
799800 .reduce_op,
800801 .call_options,
802 .prefetch_options,
801803 .export_options,
802804 .extern_options,
803805 .type_info,
......@@ -1027,6 +1029,7 @@ pub const Type = extern union {
10271029 .float_mode => return writer.writeAll("std.builtin.FloatMode"),
10281030 .reduce_op => return writer.writeAll("std.builtin.ReduceOp"),
10291031 .call_options => return writer.writeAll("std.builtin.CallOptions"),
1032 .prefetch_options => return writer.writeAll("std.builtin.PrefetchOptions"),
10301033 .export_options => return writer.writeAll("std.builtin.ExportOptions"),
10311034 .extern_options => return writer.writeAll("std.builtin.ExternOptions"),
10321035 .type_info => return writer.writeAll("std.builtin.TypeInfo"),
......@@ -1318,6 +1321,7 @@ pub const Type = extern union {
13181321 .float_mode => return "FloatMode",
13191322 .reduce_op => return "ReduceOp",
13201323 .call_options => return "CallOptions",
1324 .prefetch_options => return "PrefetchOptions",
13211325 .export_options => return "ExportOptions",
13221326 .extern_options => return "ExternOptions",
13231327 .type_info => return "TypeInfo",
......@@ -1376,6 +1380,7 @@ pub const Type = extern union {
13761380 .float_mode,
13771381 .reduce_op,
13781382 .call_options,
1383 .prefetch_options,
13791384 .export_options,
13801385 .extern_options,
13811386 .manyptr_u8,
......@@ -1502,6 +1507,7 @@ pub const Type = extern union {
15021507 .float_mode => return Value.initTag(.float_mode_type),
15031508 .reduce_op => return Value.initTag(.reduce_op_type),
15041509 .call_options => return Value.initTag(.call_options_type),
1510 .prefetch_options => return Value.initTag(.prefetch_options_type),
15051511 .export_options => return Value.initTag(.export_options_type),
15061512 .extern_options => return Value.initTag(.extern_options_type),
15071513 .type_info => return Value.initTag(.type_info_type),
......@@ -1563,6 +1569,7 @@ pub const Type = extern union {
15631569 .float_mode,
15641570 .reduce_op,
15651571 .call_options,
1572 .prefetch_options,
15661573 .export_options,
15671574 .extern_options,
15681575 .@"anyframe",
......@@ -1750,6 +1757,7 @@ pub const Type = extern union {
17501757 .float_mode,
17511758 .reduce_op,
17521759 .call_options,
1760 .prefetch_options,
17531761 .export_options,
17541762 .extern_options,
17551763 => return 1,
......@@ -1929,6 +1937,7 @@ pub const Type = extern union {
19291937 .var_args_param => unreachable,
19301938 .generic_poison => unreachable,
19311939 .call_options => unreachable, // missing call to resolveTypeFields
1940 .prefetch_options => unreachable, // missing call to resolveTypeFields
19321941 .export_options => unreachable, // missing call to resolveTypeFields
19331942 .extern_options => unreachable, // missing call to resolveTypeFields
19341943 .type_info => unreachable, // missing call to resolveTypeFields
......@@ -2269,6 +2278,7 @@ pub const Type = extern union {
22692278 .float_mode,
22702279 .reduce_op,
22712280 .call_options,
2281 .prefetch_options,
22722282 .export_options,
22732283 .extern_options,
22742284 .type_info,
......@@ -2794,6 +2804,7 @@ pub const Type = extern union {
27942804 .float_mode,
27952805 .reduce_op,
27962806 .call_options,
2807 .prefetch_options,
27972808 .export_options,
27982809 .extern_options,
27992810 .type_info,
......@@ -3294,6 +3305,7 @@ pub const Type = extern union {
32943305 .float_mode,
32953306 .reduce_op,
32963307 .call_options,
3308 .prefetch_options,
32973309 .export_options,
32983310 .extern_options,
32993311 .type_info,
......@@ -3502,6 +3514,7 @@ pub const Type = extern union {
35023514 .float_mode,
35033515 .reduce_op,
35043516 .call_options,
3517 .prefetch_options,
35053518 .export_options,
35063519 .extern_options,
35073520 => @panic("TODO resolve std.builtin types"),
......@@ -3577,6 +3590,7 @@ pub const Type = extern union {
35773590 .float_mode,
35783591 .reduce_op,
35793592 .call_options,
3593 .prefetch_options,
35803594 .export_options,
35813595 .extern_options,
35823596 => @panic("TODO resolve std.builtin types"),
......@@ -3701,6 +3715,7 @@ pub const Type = extern union {
37013715 .float_mode,
37023716 .reduce_op,
37033717 .call_options,
3718 .prefetch_options,
37043719 .export_options,
37053720 .extern_options,
37063721 .type_info,
......@@ -3741,6 +3756,7 @@ pub const Type = extern union {
37413756 .float_mode,
37423757 .reduce_op,
37433758 .call_options,
3759 .prefetch_options,
37443760 .export_options,
37453761 .extern_options,
37463762 .type_info,
......@@ -3801,6 +3817,7 @@ pub const Type = extern union {
38013817 .float_mode,
38023818 .reduce_op,
38033819 .call_options,
3820 .prefetch_options,
38043821 .export_options,
38053822 .extern_options,
38063823 => @panic("TODO resolve std.builtin types"),
......@@ -3862,6 +3879,7 @@ pub const Type = extern union {
38623879 float_mode,
38633880 reduce_op,
38643881 call_options,
3882 prefetch_options,
38653883 export_options,
38663884 extern_options,
38673885 type_info,
......@@ -3989,6 +4007,7 @@ pub const Type = extern union {
39894007 .float_mode,
39904008 .reduce_op,
39914009 .call_options,
4010 .prefetch_options,
39924011 .export_options,
39934012 .extern_options,
39944013 .type_info,
src/value.zig+5
......@@ -67,6 +67,7 @@ pub const Value = extern union {
6767 float_mode_type,
6868 reduce_op_type,
6969 call_options_type,
70 prefetch_options_type,
7071 export_options_type,
7172 extern_options_type,
7273 type_info_type,
......@@ -244,6 +245,7 @@ pub const Value = extern union {
244245 .float_mode_type,
245246 .reduce_op_type,
246247 .call_options_type,
248 .prefetch_options_type,
247249 .export_options_type,
248250 .extern_options_type,
249251 .type_info_type,
......@@ -434,6 +436,7 @@ pub const Value = extern union {
434436 .float_mode_type,
435437 .reduce_op_type,
436438 .call_options_type,
439 .prefetch_options_type,
437440 .export_options_type,
438441 .extern_options_type,
439442 .type_info_type,
......@@ -652,6 +655,7 @@ pub const Value = extern union {
652655 .float_mode_type => return out_stream.writeAll("std.builtin.FloatMode"),
653656 .reduce_op_type => return out_stream.writeAll("std.builtin.ReduceOp"),
654657 .call_options_type => return out_stream.writeAll("std.builtin.CallOptions"),
658 .prefetch_options_type => return out_stream.writeAll("std.builtin.PrefetchOptions"),
655659 .export_options_type => return out_stream.writeAll("std.builtin.ExportOptions"),
656660 .extern_options_type => return out_stream.writeAll("std.builtin.ExternOptions"),
657661 .type_info_type => return out_stream.writeAll("std.builtin.TypeInfo"),
......@@ -829,6 +833,7 @@ pub const Value = extern union {
829833 .float_mode_type => Type.initTag(.float_mode),
830834 .reduce_op_type => Type.initTag(.reduce_op),
831835 .call_options_type => Type.initTag(.call_options),
836 .prefetch_options_type => Type.initTag(.prefetch_options),
832837 .export_options_type => Type.initTag(.export_options),
833838 .extern_options_type => Type.initTag(.extern_options),
834839 .type_info_type => Type.initTag(.type_info),
test/behavior.zig+1
......@@ -169,6 +169,7 @@ test {
169169 _ = @import("behavior/optional_stage1.zig");
170170 _ = @import("behavior/pointers_stage1.zig");
171171 _ = @import("behavior/popcount_stage1.zig");
172 _ = @import("behavior/prefetch.zig");
172173 _ = @import("behavior/ptrcast_stage1.zig");
173174 _ = @import("behavior/reflection.zig");
174175 _ = @import("behavior/saturating_arithmetic_stage1.zig");
test/behavior/prefetch.zig created+27
......@@ -0,0 +1,27 @@
1const std = @import("std");
2
3test "@prefetch()" {
4 var a: u32 = 42;
5
6 @prefetch(&a, .{});
7
8 @prefetch(&a, .{ .rw = .read, .locality = 3, .cache = .data });
9 @prefetch(&a, .{ .rw = .read, .locality = 2, .cache = .data });
10 @prefetch(&a, .{ .rw = .read, .locality = 1, .cache = .data });
11 @prefetch(&a, .{ .rw = .read, .locality = 0, .cache = .data });
12
13 @prefetch(&a, .{ .rw = .write, .locality = 3, .cache = .data });
14 @prefetch(&a, .{ .rw = .write, .locality = 2, .cache = .data });
15 @prefetch(&a, .{ .rw = .write, .locality = 1, .cache = .data });
16 @prefetch(&a, .{ .rw = .write, .locality = 0, .cache = .data });
17
18 @prefetch(&a, .{ .rw = .read, .locality = 3, .cache = .instruction });
19 @prefetch(&a, .{ .rw = .read, .locality = 2, .cache = .instruction });
20 @prefetch(&a, .{ .rw = .read, .locality = 1, .cache = .instruction });
21 @prefetch(&a, .{ .rw = .read, .locality = 0, .cache = .instruction });
22
23 @prefetch(&a, .{ .rw = .write, .locality = 3, .cache = .instruction });
24 @prefetch(&a, .{ .rw = .write, .locality = 2, .cache = .instruction });
25 @prefetch(&a, .{ .rw = .write, .locality = 1, .cache = .instruction });
26 @prefetch(&a, .{ .rw = .write, .locality = 0, .cache = .instruction });
27}