feat: (CM64) context.{set, get} accept i64 immediate#2502
feat: (CM64) context.{set, get} accept i64 immediate#2502michael-weigelt wants to merge 3 commits intobytecodealliance:mainfrom
context.{set, get} accept i64 immediate#2502Conversation
| ;; Other value types (e.g. `f32`) are rejected regardless of the `cm64` feature. | ||
| (assert_invalid | ||
| (component | ||
| (core func (canon context.get f32 0))) | ||
| "`context.get` only supports `i32` or `i64`") | ||
| (assert_invalid | ||
| (component | ||
| (core func (canon context.set f32 0))) | ||
| "`context.set` only supports `i32` or `i64`") |
There was a problem hiding this comment.
Moving the discussion from https://github.com/michael-weigelt/wasm-tools/pull/2#discussion_r3129304318 to here:
There was a problem hiding this comment.
adambratschikaye
I think these ones should be malformed instead of invalid because the spec doesn't even permit the construction with other values. I guess this means you need to have a check in the parser itself as well.
michael-weigelt
Ah I wrongly assumed that this is extensible, coming from the resource rep type. Then maybe I should not use ValType anywhere at all. (?)
There was a problem hiding this comment.
This is a good question! By the letter of the spec here @adambratschikaye is correct where these tests should be assert_malformed rather than assert_invalid. Additionally following the letter of the spec all usage of ValType would want to get replaced with something else, yeah. (in Wasmtime we have an IndexType for memories/tables which may be another possible name).
cc @lukewagner on this specific point though, how do you feel about this? Should the binary format for context.{get,set} allow any core wasm value type in the binary encoding (and, thus, the text encoding too)? Or should the binary/text formats only allow i32 and i64? I could see us having a desire in the future to allow something like anyref here too for GC-using programs, in which case it'd be easiest to relax and allow any core value type here with validation restricting it to i32 and i64.
There was a problem hiding this comment.
In the binary format, we use the <valtype> opcodes for i32 and i64 (0x7f and 0x7e) directly in the decoding rule instead of decoding a generic <valtype> and then using validation rules to rule out everything except i32/i64 (just like we do for the (rep i32)/(rep i64) of a resource type). But in the AST, we have (canon context.{get,set} <valtype> ...) and verbiage about validation rejecting everything but i32/i64, so I suppose it's ambiguous. Probably we should align Binary.md with Explainer.md and go the decode+validate route, hence assert_invalid?
Practically speaking, I hope we don't do anyref since it'll raise gross questions of what to do if you, e.g., context.set anyref 0 and then context.get i32 0. With integers we say that they alias like 64-bit registers do on x64, but less clear what the right thing to do with anyref that avoids overhead. My thinking was that, if you wanted to store a reference, you'd store it in a table and then store the i32 index in TLS.
There was a problem hiding this comment.
I hadn't thought about the view-typing of slot 0 and slot 1 as different integer widths and that's a good point... Do you think it's feasible that we could guarantee that each slot is viewed as exactly one type in validation? For example within a component context.get 0 could only have one type?
Otherwise though I agree that syncing the text format and Binary.md makes sense, namely by changing Binary.md to use a valtype, which @michael-weigelt would keep this as assert_invalid yeah.
There was a problem hiding this comment.
Do you think it's feasible that we could guarantee that each slot is viewed as exactly one type in validation?
That's a good point; I suppose we could add a whole-component validation rule to that effect. It'd be the only validation rule like that and usually a rule like that has composability problems (compiling unrelated bits of code that disagree), but given that the use of context.{get,set} requires all the code in the same component to agree on the ABI anyways, I think it'd be be fine in this case. I think I'll work on a CM PR for this and the Binary.md fix.
There was a problem hiding this comment.
Makes sense, and sounds good! In that case @michael-weigelt let's keep all the ValType bits as-is in this PR
| fn type_name(ty: ValType) -> &'static str { | ||
| match ty { | ||
| ValType::I32 => "i32", | ||
| ValType::I64 => "i64", | ||
| _ => "<invalid>", | ||
| } | ||
| } |
There was a problem hiding this comment.
Should I define a new type instead of using ValType? Should we have a proper "MemoryType" that is future-proof? But then I am not sure if encoding/decoding will be more difficult, as that uses the known byte representation of i32/i64.
There was a problem hiding this comment.
There are several more awkward matches like this...
There was a problem hiding this comment.
For now, since this is just a debug name, I think putting "whatever" here is fine, including dropping the value type entirely. In a debugging context for a particular module the value type probably isn't too interesting as the most interesting part is just that it's context.{get,set} in general. Putting <invalid> isn't the end of the world but if we stick with ValType and end up relaxing this in the future we'll almost surely forget to update this which is why I'd lean towards just dropping this part of the name for now.
| /// A `context.get` intrinsic for the `i`th slot of task-local storage. | ||
| ContextGet(u32), | ||
| ContextGet { | ||
| /// The type of the slot. Currently only `ValType::I32` and |
There was a problem hiding this comment.
The "currently" should go, unless we know we want to extend this.
| ContextGet(crate::core::ValType<'a>, u32), | ||
| ContextSet(crate::core::ValType<'a>, u32), |
There was a problem hiding this comment.
I could introduce a CanonContextSet if that is preferred.
| self.canon_opts(&mut info.opts)?; | ||
| } | ||
| CoreFuncKind::ContextGet(_) | CoreFuncKind::ContextSet(_) => {} | ||
| CoreFuncKind::ContextGet(..) | CoreFuncKind::ContextSet(..) => {} |
There was a problem hiding this comment.
I think that technically this'll need to ge handled here with a resolve_ns one way or another. Otherwise specifying (ref $some-core-type) I think would panic during binary encoding
| /// Parses a `[context-get-<N>]` / `[context-set-<N>]` style name, optionally | ||
| /// carrying a type width infix: `[context-get-i64-<N>]`. | ||
| /// | ||
| /// Returns the value type together with the numeric slot. Additional type | ||
| /// widths can be added here by extending the match below. | ||
| fn parse_context_name(name: &str, prefix: &str) -> Option<(ValType, u32)> { | ||
| let (suffix, rest) = prefixed_intrinsic(name, prefix)?; | ||
| let n = suffix.parse().ok()?; | ||
| Some((n, rest)) | ||
| if !rest.is_empty() { | ||
| return None; | ||
| } | ||
| let (ty, slot) = match suffix.split_once('-') { | ||
| Some(("i64", slot)) => (ValType::I64, slot), | ||
| Some(("i32", slot)) => (ValType::I32, slot), | ||
| _ => (ValType::I32, "unreachable"), | ||
| }; | ||
| let slot = slot.parse().ok()?; | ||
| Some((ty, slot)) | ||
| } |
There was a problem hiding this comment.
While context.{get,set} is currently gated behind component-model-async and technically not shipped we're late enough in the process that we can't easily change the names here. Given that, could this accept both the old name (e.g. [context-get-0]) but additionally accept new names (e.g. [context-get-{i32,i64}-0])? That way we can transition consumers to the new names and possibly remove support for the old names in the future
| /// Translates the `context.get`/`context.set` slot type recorded by the | ||
| /// validator into the `wasm_encoder::ValType` consumed by the component | ||
| /// builder. Only `i32` and `i64` currently make it past the validator; anything | ||
| /// else is a bug. | ||
| fn context_val_type(ty: wasmparser::ValType) -> ValType { | ||
| match ty { | ||
| wasmparser::ValType::I32 => ValType::I32, | ||
| wasmparser::ValType::I64 => ValType::I64, | ||
| _ => unreachable!( | ||
| "context.get/set slot type `{ty}` should have been rejected by the validator" | ||
| ), | ||
| } | ||
| } |
There was a problem hiding this comment.
Could this perhaps use the TryFrom impl to avoid the panic here?
| ContextGet(crate::core::ValType<'a>, u32), | ||
| ContextSet(crate::core::ValType<'a>, u32), |
| ;; Other value types (e.g. `f32`) are rejected regardless of the `cm64` feature. | ||
| (assert_invalid | ||
| (component | ||
| (core func (canon context.get f32 0))) | ||
| "`context.get` only supports `i32` or `i64`") | ||
| (assert_invalid | ||
| (component | ||
| (core func (canon context.set f32 0))) | ||
| "`context.set` only supports `i32` or `i64`") |
There was a problem hiding this comment.
This is a good question! By the letter of the spec here @adambratschikaye is correct where these tests should be assert_malformed rather than assert_invalid. Additionally following the letter of the spec all usage of ValType would want to get replaced with something else, yeah. (in Wasmtime we have an IndexType for memories/tables which may be another possible name).
cc @lukewagner on this specific point though, how do you feel about this? Should the binary format for context.{get,set} allow any core wasm value type in the binary encoding (and, thus, the text encoding too)? Or should the binary/text formats only allow i32 and i64? I could see us having a desire in the future to allow something like anyref here too for GC-using programs, in which case it'd be easiest to relax and allow any core value type here with validation restricting it to i32 and i64.
| fn type_name(ty: ValType) -> &'static str { | ||
| match ty { | ||
| ValType::I32 => "i32", | ||
| ValType::I64 => "i64", | ||
| _ => "<invalid>", | ||
| } | ||
| } |
There was a problem hiding this comment.
For now, since this is just a debug name, I think putting "whatever" here is fine, including dropping the value type entirely. In a debugging context for a particular module the value type probably isn't too interesting as the most interesting part is just that it's context.{get,set} in general. Putting <invalid> isn't the end of the world but if we stick with ValType and end up relaxing this in the future we'll almost surely forget to update this which is why I'd lean towards just dropping this part of the name for now.
This is part of a series of PRs to enable verification of 64bit components.. The effort is tracked here.
This PR allows
context.{set, get}to accepti64as well asi32.