Skip to content

Commit

Permalink
Merge pull request #10295 from ifreund/prefetch
Browse files Browse the repository at this point in the history
Implement @prefetch()
  • Loading branch information
andrewrk committed Dec 11, 2021
2 parents 75f3e7a + 516945d commit 97c0373
Show file tree
Hide file tree
Showing 16 changed files with 395 additions and 7 deletions.
44 changes: 44 additions & 0 deletions doc/langref.html.in
Expand Up @@ -8674,6 +8674,50 @@ test "@wasmMemoryGrow" {
{#see_also|@ctz|@clz#}
{#header_close#}

{#header_open|@prefetch#}
<pre>{#syntax#}@prefetch(ptr: anytype, comptime options: std.builtin.PrefetchOptions){#endsyntax#}</pre>
<p>
This builtin tells the compiler to emit a prefetch instruction if supported by the
target CPU. If the target CPU does not support the requested prefetch instruction,
this builtin is a noop. This function has no effect on the behavior of the program,
only on the performance characteristics.
</p>
<p>
The {#syntax#}ptr{#endsyntax#} argument may be any pointer type and determines the memory
address to prefetch. This function does not dereference the pointer, it is perfectly legal
to pass a pointer to invalid memory to this function and no illegal behavior will result.
</p>
<p>
The {#syntax#}options{#endsyntax#} argument is the following struct:
</p>
{#code_begin|syntax|builtin#}
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const PrefetchOptions = struct {
/// Whether the prefetch should prepare for a read or a write.
rw: Rw = .read,
/// 0 means no temporal locality. That is, the data can be immediately
/// dropped from the cache after it is accessed.
///
/// 3 means high temporal locality. That is, the data should be kept in
/// the cache as it is likely to be accessed again soon.
locality: u2 = 3,
/// The cache that the prefetch should be preformed on.
cache: Cache = .data,

pub const Rw = enum {
read,
write,
};

pub const Cache = enum {
instruction,
data,
};
};
{#code_end#}
{#header_close#}

{#header_open|@ptrCast#}
<pre>{#syntax#}@ptrCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre>
<p>
Expand Down
25 changes: 25 additions & 0 deletions lib/std/builtin.zig
Expand Up @@ -651,6 +651,31 @@ pub const CallOptions = struct {
};
};

/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const PrefetchOptions = struct {
/// Whether the prefetch should prepare for a read or a write.
rw: Rw = .read,
/// 0 means no temporal locality. That is, the data can be immediately
/// dropped from the cache after it is accessed.
///
/// 3 means high temporal locality. That is, the data should be kept in
/// the cache as it is likely to be accessed again soon.
locality: u2 = 3,
/// The cache that the prefetch should be preformed on.
cache: Cache = .data,

pub const Rw = enum {
read,
write,
};

pub const Cache = enum {
instruction,
data,
};
};

/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const ExportOptions = struct {
Expand Down
26 changes: 20 additions & 6 deletions src/AstGen.zig
Expand Up @@ -6739,7 +6739,6 @@ fn builtinCall(
}
}

// zig fmt: off
switch (info.tag) {
.import => {
const node_tags = tree.nodes.items(.tag);
Expand Down Expand Up @@ -6768,7 +6767,7 @@ fn builtinCall(
astgen.extra.items[extra_index] = @enumToInt(param_ref);
extra_index += 1;
}
const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log,payload_index, params.len);
const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
return rvalue(gz, rl, result, node);
},
.field => {
Expand All @@ -6784,11 +6783,14 @@ fn builtinCall(
});
return rvalue(gz, rl, result, node);
},

// zig fmt: off
.as => return as( gz, scope, rl, node, params[0], params[1]),
.bit_cast => return bitCast( gz, scope, rl, node, params[0], params[1]),
.TypeOf => return typeOf( gz, scope, rl, node, params),
.union_init => return unionInit(gz, scope, rl, node, params),
.c_import => return cImport( gz, scope, node, params[0]),
// zig fmt: on

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

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

// zig fmt: off
.This => return rvalue(gz, rl, try gz.addNodeExtended(.this, node), node),
.return_address => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr, node), node),
.src => return rvalue(gz, rl, try gz.addNodeExtended(.builtin_src, node), node),
Expand Down Expand Up @@ -6942,6 +6943,8 @@ fn builtinCall(
.err_set_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .err_set_cast),
.ptr_cast => return typeCast(gz, scope, rl, node, params[0], params[1], .ptr_cast),
.truncate => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate),
// zig fmt: on

.align_cast => {
const dest_align = try comptimeExpr(gz, scope, align_rl, params[0]);
const rhs = try expr(gz, scope, .none, params[1]);
Expand All @@ -6952,6 +6955,7 @@ fn builtinCall(
return rvalue(gz, rl, result, node);
},

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

Expand All @@ -6978,6 +6982,7 @@ fn builtinCall(

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

.wasm_memory_size => {
const operand = try expr(gz, scope, .{ .ty = .u32_type }, params[0]);
Expand Down Expand Up @@ -7221,8 +7226,17 @@ fn builtinCall(
});
return rvalue(gz, rl, result, node);
},
.prefetch => {
const ptr = try expr(gz, scope, .none, params[0]);
const options = try comptimeExpr(gz, scope, .{ .ty = .prefetch_options_type }, params[1]);
const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
.node = gz.nodeIndexToRelative(node),
.lhs = ptr,
.rhs = options,
});
return rvalue(gz, rl, result, node);
},
}
// zig fmt: on
}

fn simpleNoOpVoid(
Expand Down
8 changes: 8 additions & 0 deletions src/BuiltinFn.zig
Expand Up @@ -67,6 +67,7 @@ pub const Tag = enum {
mul_with_overflow,
panic,
pop_count,
prefetch,
ptr_cast,
ptr_to_int,
rem,
Expand Down Expand Up @@ -615,6 +616,13 @@ pub const list = list: {
.param_count = 2,
},
},
.{
"@prefetch",
.{
.tag = .prefetch,
.param_count = 2,
},
},
.{
"@ptrCast",
.{
Expand Down
14 changes: 14 additions & 0 deletions src/Sema.zig
Expand Up @@ -1042,6 +1042,7 @@ fn zirExtended(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
.c_define => return sema.zirCDefine( block, extended),
.wasm_memory_size => return sema.zirWasmMemorySize( block, extended),
.wasm_memory_grow => return sema.zirWasmMemoryGrow( block, extended),
.prefetch => return sema.zirPrefetch( block, extended),
// zig fmt: on
}
}
Expand Down Expand Up @@ -11104,6 +11105,16 @@ fn zirWasmMemoryGrow(
return sema.fail(block, src, "TODO: implement Sema.zirWasmMemoryGrow", .{});
}

fn zirPrefetch(
sema: *Sema,
block: *Block,
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", .{});
}

fn zirBuiltinExtern(
sema: *Sema,
block: *Block,
Expand Down Expand Up @@ -14231,6 +14242,7 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
.float_mode => return sema.resolveBuiltinTypeFields(block, src, "FloatMode"),
.reduce_op => return sema.resolveBuiltinTypeFields(block, src, "ReduceOp"),
.call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
.prefetch_options => return sema.resolveBuiltinTypeFields(block, src, "PrefetchOptions"),

.@"union", .union_tagged => {
const union_obj = ty.cast(Type.Payload.Union).?.data;
Expand Down Expand Up @@ -14819,6 +14831,7 @@ fn typeHasOnePossibleValue(
.float_mode,
.reduce_op,
.call_options,
.prefetch_options,
.export_options,
.extern_options,
.type_info,
Expand Down Expand Up @@ -15032,6 +15045,7 @@ pub fn addType(sema: *Sema, ty: Type) !Air.Inst.Ref {
.float_mode => return .float_mode_type,
.reduce_op => return .reduce_op_type,
.call_options => return .call_options_type,
.prefetch_options => return .prefetch_options_type,
.export_options => return .export_options_type,
.extern_options => return .extern_options_type,
.type_info => return .type_info_type,
Expand Down
8 changes: 8 additions & 0 deletions src/Zir.zig
Expand Up @@ -1572,6 +1572,9 @@ pub const Inst = struct {
wasm_memory_size,
/// `operand` is payload index to `BinNode`.
wasm_memory_grow,
/// The `@prefetch` builtin.
/// `operand` is payload index to `BinNode`.
prefetch,

pub const InstData = struct {
opcode: Extended,
Expand Down Expand Up @@ -1648,6 +1651,7 @@ pub const Inst = struct {
float_mode_type,
reduce_op_type,
call_options_type,
prefetch_options_type,
export_options_type,
extern_options_type,
type_info_type,
Expand Down Expand Up @@ -1917,6 +1921,10 @@ pub const Inst = struct {
.ty = Type.initTag(.type),
.val = Value.initTag(.call_options_type),
},
.prefetch_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.prefetch_options_type),
},
.export_options_type = .{
.ty = Type.initTag(.type),
.val = Value.initTag(.export_options_type),
Expand Down
2 changes: 1 addition & 1 deletion src/print_zir.zig
Expand Up @@ -477,7 +477,7 @@ const Writer = struct {
try self.writeSrc(stream, src);
},

.builtin_extern, .c_define, .wasm_memory_grow => {
.builtin_extern, .c_define, .wasm_memory_grow, .prefetch => {
const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data;
const src: LazySrcLoc = .{ .node_offset = inst_data.node };
try self.writeInstRef(stream, inst_data.lhs);
Expand Down
34 changes: 34 additions & 0 deletions src/stage1/all_types.hpp
Expand Up @@ -898,6 +898,18 @@ struct AstNodeFnCallExpr {
bool seen; // used by @compileLog
};

// Must be kept in sync with std.builtin.PrefetchOptions.Rw
enum PrefetchRw {
PrefetchRwRead,
PrefetchRwWrite,
};

// Must be kept in sync with std.builtin.PrefetchOptions.Cache
enum PrefetchCache {
PrefetchCacheInstruction,
PrefetchCacheData,
};

struct AstNodeArrayAccessExpr {
AstNode *array_ref_expr;
AstNode *subscript;
Expand Down Expand Up @@ -1818,6 +1830,7 @@ enum BuiltinFnId {
BuiltinFnIdReduce,
BuiltinFnIdMaximum,
BuiltinFnIdMinimum,
BuiltinFnIdPrefetch,
};

struct BuiltinFnEntry {
Expand Down Expand Up @@ -2021,6 +2034,7 @@ struct CodeGen {
LLVMValueRef return_err_fn;
LLVMValueRef wasm_memory_size;
LLVMValueRef wasm_memory_grow;
LLVMValueRef prefetch;
LLVMTypeRef anyframe_fn_type;

// reminder: hash tables must be initialized before use
Expand Down Expand Up @@ -2647,6 +2661,7 @@ enum Stage1ZirInstId : uint8_t {
Stage1ZirInstIdWasmMemorySize,
Stage1ZirInstIdWasmMemoryGrow,
Stage1ZirInstIdSrc,
Stage1ZirInstIdPrefetch,
};

// ir_render_* functions in codegen.cpp consume Gen instructions and produce LLVM IR.
Expand Down Expand Up @@ -2743,6 +2758,7 @@ enum Stage1AirInstId : uint8_t {
Stage1AirInstIdWasmMemorySize,
Stage1AirInstIdWasmMemoryGrow,
Stage1AirInstIdExtern,
Stage1AirInstIdPrefetch,
};

struct Stage1ZirInst {
Expand Down Expand Up @@ -3683,6 +3699,24 @@ struct Stage1ZirInstSrc {
Stage1ZirInst base;
};

struct Stage1ZirInstPrefetch {
Stage1ZirInst base;

Stage1ZirInst *ptr;
Stage1ZirInst *options;
};

struct Stage1AirInstPrefetch {
Stage1AirInst base;

Stage1AirInst *ptr;
PrefetchRw rw;
// Must be in the range 0-3 inclusive
uint8_t locality;
PrefetchCache cache;
};


struct Stage1ZirInstSlice {
Stage1ZirInst base;

Expand Down

0 comments on commit 97c0373

Please sign in to comment.