Stream: git-wasmtime

Topic: wasmtime / issue #3900 Cranelift: split out a "settings" ...


view this post on Zulip Wasmtime GitHub notifications bot (Mar 08 2022 at 21:45):

cfallin opened issue #3900:

In #3897, we discovered that Wasmtime's checking of a serialized/AOT-compiled module's flags for compatibility was not running if the runtime was not built with the compiler included. #3899 hardcodes some checks in a way that still works without Cranelift, so solves the immediate problem, but is not an ideal fix.

Ultimately we should be able to talk about the settings/configuration for the compiler without actually including the compiler itself. In the same way that Target is a type defined in a separate crate (target-lexicon), we should create a new crate cranelift-settings that lives at the bottom of the crate dependency DAG, and can be used without Cranelift itself. (It will probably depend on the settings stuff that is generated by cranelift-codegen's meta crate currently, so we'll have to figure out how to split things apart appropriately.)

This crate should have a way of comparing flags as well. Each flag has some property of "ABI compatibility" which is a sort of subtyping relation: for some flags, code generated with that flag taking on one value is compatible with the runtime assuming the flag has another value. For example, optimization level or regalloc selection don't affect the generated code-to-runtime interface. Nor should settings that affect whether certain ISA extensions are enabled. But, for example, the existence of safepoints in the generated code does affect the runtime; if code is generated without these, its execution will be incorrect if the runtime is providing reftypes.

So we should have a CompilerFlags and it should support a partial order relation which computes this compatibility.

Then, the cranelift-native crate can generate some flags that correspond to the maximum CPU features on the host, and wasmtime-cranelift can also generate a CompilerFlags, indicating which flags are required for a given Wasmtime config (e.g., safepoints, or fuel or epoch instrumentation, etc.). We should also have a way to merge flags; this is a "meet" on the lattice induced by the partial ordering relation above.

Then finally, wasmtime's compatibility checks and compile-time flag selection can use this infrastructure, rather than the hardcoded feature-compatibility matrices encoded into the Engine and serialization logic.

view this post on Zulip Wasmtime GitHub notifications bot (Mar 08 2022 at 21:47):

cfallin edited issue #3900:

In #3897, we discovered that Wasmtime's checking of a serialized/AOT-compiled module's flags for compatibility was not running if the runtime was not built with the compiler included. #3899 hardcodes some checks in a way that still works without Cranelift, so solves the immediate problem, but is not an ideal fix.

Ultimately we should be able to talk about the settings/configuration for the compiler without actually including the compiler itself. In the same way that Target is a type defined in a separate crate (target-lexicon), we should create a new crate cranelift-settings that lives at the bottom of the crate dependency DAG, and can be used without Cranelift itself. (It will probably depend on the settings stuff that is generated by cranelift-codegen's meta crate currently, so we'll have to figure out how to split things apart appropriately.)

This crate should have a way of comparing flags as well. Each flag has some property of "ABI compatibility" which is a sort of subtyping relation: for some flags, code generated with that flag taking on one value is compatible with the runtime assuming the flag has another value. For example, optimization level or regalloc selection don't affect the generated code-to-runtime interface. Nor should settings that affect whether certain ISA extensions are enabled. But, for example, the existence of safepoints in the generated code does affect the runtime; if code is generated without these, its execution will be incorrect if the runtime is providing reftypes.

So we should have a CompilerFlags and it should support a partial order relation which computes this compatibility.

Then, the cranelift-native crate can generate some flags that correspond to the maximum CPU features on the host, and wasmtime-cranelift can also generate a CompilerFlags, indicating which flags are required for a given Wasmtime config (e.g., safepoints, or fuel or epoch instrumentation, etc.). We should also have a way to merge flags; this is a "meet" on the lattice induced by the partial ordering relation above. (Slight subtlety: the lattice is a product lattice of an individual lattice per flag; each individual flag also needs a "top" (don't care) and "bottom" (conflict) value.)

Then finally, wasmtime's compatibility checks and compile-time flag selection can use this infrastructure, rather than the hardcoded feature-compatibility matrices encoded into the Engine and serialization logic.

view this post on Zulip Wasmtime GitHub notifications bot (Mar 09 2022 at 15:52):

alexcrichton commented on issue #3900:

One thing worth pointing out is that prior to https://github.com/bytecodealliance/wasmtime/pull/3899 we actually had no checks at all that when a Module was instantiated that the compiler settings configured in the Engine were actually compatible with the host platform (e.g. enabled CPU features were actually available), and this was also one thing that #3899 wanted to fix.

Another thing I think worth considering is that prior to #3899 when a serialized module was loaded from disk into a Wasmtime with Cranelift enabled we'd compare all the compilation settings to ensure that they exactly match the current compiler. There wasn't actually any form of subtyping/etc in the sense of testing whether code is ABI-compatible. Personally I think such a heuristic is good enough as I don't think we know of any use case of having specifically different compiler settings and still being able to load the module into compatible engines.

One example of this is that you mentioned epochs/fuel and those aren't actually related to cranelift compiler settings, that's more of Wasmtime's own Tunables which is serialized/deserialized and tested for absolute equality. While most of those settings matter for runtime code settings such as generate_address_map or dynamic_memory_growth_reserve don't actually affect anything and modules compiled with one version of those settings could be loaded into engines with a different version of the setting.

All that to say that the main issue with #3899 was dealing with the fact that we have to rationalize cranelift flags in the face of cranelift not being available. The logic I implemented in #3899 was my best attempt to interpret cranelift settings, but if we had a separate crate I think we could largely get away with everything having to be configured exactly the same way. I suspect we don't need to implement per-configuration-setting "is this compatible with other values at the ABI level" configuration (as that would probably be sort of a pain to maintain in the limit as well). All of this would still require extraction to a separate crate though so I think that has to happen regardless, I'm mostly just pointing out that I don't think the subtyping aspect is strictly necessary (or rather I'd like to modify things to ensure that it's not strictly necessary).

view this post on Zulip Wasmtime GitHub notifications bot (Mar 23 2022 at 20:12):

alexcrichton labeled issue #3900:

In #3897, we discovered that Wasmtime's checking of a serialized/AOT-compiled module's flags for compatibility was not running if the runtime was not built with the compiler included. #3899 hardcodes some checks in a way that still works without Cranelift, so solves the immediate problem, but is not an ideal fix.

Ultimately we should be able to talk about the settings/configuration for the compiler without actually including the compiler itself. In the same way that Target is a type defined in a separate crate (target-lexicon), we should create a new crate cranelift-settings that lives at the bottom of the crate dependency DAG, and can be used without Cranelift itself. (It will probably depend on the settings stuff that is generated by cranelift-codegen's meta crate currently, so we'll have to figure out how to split things apart appropriately.)

This crate should have a way of comparing flags as well. Each flag has some property of "ABI compatibility" which is a sort of subtyping relation: for some flags, code generated with that flag taking on one value is compatible with the runtime assuming the flag has another value. For example, optimization level or regalloc selection don't affect the generated code-to-runtime interface. Nor should settings that affect whether certain ISA extensions are enabled. But, for example, the existence of safepoints in the generated code does affect the runtime; if code is generated without these, its execution will be incorrect if the runtime is providing reftypes.

So we should have a CompilerFlags and it should support a partial order relation which computes this compatibility.

Then, the cranelift-native crate can generate some flags that correspond to the maximum CPU features on the host, and wasmtime-cranelift can also generate a CompilerFlags, indicating which flags are required for a given Wasmtime config (e.g., safepoints, or fuel or epoch instrumentation, etc.). We should also have a way to merge flags; this is a "meet" on the lattice induced by the partial ordering relation above. (Slight subtlety: the lattice is a product lattice of an individual lattice per flag; each individual flag also needs a "top" (don't care) and "bottom" (conflict) value.)

Then finally, wasmtime's compatibility checks and compile-time flag selection can use this infrastructure, rather than the hardcoded feature-compatibility matrices encoded into the Engine and serialization logic.

view this post on Zulip Wasmtime GitHub notifications bot (May 04 2022 at 21:06):

cfallin labeled issue #3900:

In #3897, we discovered that Wasmtime's checking of a serialized/AOT-compiled module's flags for compatibility was not running if the runtime was not built with the compiler included. #3899 hardcodes some checks in a way that still works without Cranelift, so solves the immediate problem, but is not an ideal fix.

Ultimately we should be able to talk about the settings/configuration for the compiler without actually including the compiler itself. In the same way that Target is a type defined in a separate crate (target-lexicon), we should create a new crate cranelift-settings that lives at the bottom of the crate dependency DAG, and can be used without Cranelift itself. (It will probably depend on the settings stuff that is generated by cranelift-codegen's meta crate currently, so we'll have to figure out how to split things apart appropriately.)

This crate should have a way of comparing flags as well. Each flag has some property of "ABI compatibility" which is a sort of subtyping relation: for some flags, code generated with that flag taking on one value is compatible with the runtime assuming the flag has another value. For example, optimization level or regalloc selection don't affect the generated code-to-runtime interface. Nor should settings that affect whether certain ISA extensions are enabled. But, for example, the existence of safepoints in the generated code does affect the runtime; if code is generated without these, its execution will be incorrect if the runtime is providing reftypes.

So we should have a CompilerFlags and it should support a partial order relation which computes this compatibility.

Then, the cranelift-native crate can generate some flags that correspond to the maximum CPU features on the host, and wasmtime-cranelift can also generate a CompilerFlags, indicating which flags are required for a given Wasmtime config (e.g., safepoints, or fuel or epoch instrumentation, etc.). We should also have a way to merge flags; this is a "meet" on the lattice induced by the partial ordering relation above. (Slight subtlety: the lattice is a product lattice of an individual lattice per flag; each individual flag also needs a "top" (don't care) and "bottom" (conflict) value.)

Then finally, wasmtime's compatibility checks and compile-time flag selection can use this infrastructure, rather than the hardcoded feature-compatibility matrices encoded into the Engine and serialization logic.


Last updated: Nov 22 2024 at 16:03 UTC