-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WebAssembly: simd128 feature is required even when the relaxed-simd feature is enabled #98502
Comments
cc @tlively |
@llvm/issue-subscribers-backend-webassembly Author: Alex Crichton (alexcrichton)
Given this input:
target triple = "wasm32-unknown-wasi"
declare <2 x i64> @<!-- -->llvm.wasm.relaxed.laneselect.v2i64(<2 x i64>, <2 x i64>, <2 x i64>)
define <2 x i64> @<!-- -->test(<2 x i64>, <2 x i64>, <2 x i64>) #<!-- -->0 {
start:
%_4 = tail call <2 x i64> @<!-- -->llvm.wasm.relaxed.laneselect.v2i64(<2 x i64> %0, <2 x i64> %1, <2 x i64> %2) #<!-- -->3
ret <2 x i64> %_4
}
attributes #<!-- -->0 = { "target-features"="+relaxed-simd" } this currently crashes with:
This works if |
You're saying that maybe enabling the relaxed-simd feature should implicitly enable the simd128 feature, rather than requiring it to be explicit? |
Ideally yeah I think that'd be a good state. I thought that was the intention already but it may not be. I believe in x86 if you enable sse4.2 for example it auto-enables sse4.1 and prior, but I'm not sure by what mechanism that happens. Or at least that's what I'm led to believe which is where my pseudo-expectation for enabling simd128 automatically comes from. If this goes the other way and it's an intentional error then frontends will need to be updated so that when users enable |
@aheejin we must do this for exceptions too, right (i.e. implicitly enable reference types when enabling exnref). I'm guessing that happens in the frontend though, since the frontend is already coordinating several different flags. |
I know the clang frontend driver has a “feature tower” so simd128 will be automatically enabled if you use relaxed-simd, but I guess we don’t do that in the backend. The intention is to follow whatever pattern other backends use. |
@alexcrichton Even though we check this as you pointed out,
The reason this fails is we query the feature set directly here: llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp Lines 644 to 645 in d0d05ae
This void verifyInstructionPredicates(
unsigned Opcode, const FeatureBitset &Features) {
#ifndef NDEBUG
FeatureBitset AvailableFeatures = computeAvailableFeatures(Features);
FeatureBitset RequiredFeatures = computeRequiredFeatures(Opcode);
FeatureBitset MissingFeatures =
(AvailableFeatures & RequiredFeatures) ^
RequiredFeatures;
if (MissingFeatures.any()) {
std::ostringstream Msg;
Msg << "Attempting to emit " << &WebAssemblyInstrNameData[WebAssemblyInstrNameIndices[Opcode]]
<< " instruction but the ";
for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)
if (MissingFeatures.test(i))
Msg << SubtargetFeatureNames[i] << " ";
Msg << "predicate(s) are not met";
report_fatal_error(Msg.str().c_str());
}
#endif // NDEBUG
} And inline FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) {
FeatureBitset Features;
if (FB[WebAssembly::FeatureAtomics])
Features.set(Feature_HasAtomicsBit);
if (FB[WebAssembly::FeatureBulkMemory])
Features.set(Feature_HasBulkMemoryBit);
if (FB[WebAssembly::FeatureExceptionHandling])
Features.set(Feature_HasExceptionHandlingBit);
... And it looks other targets' AsmPrinter checks the feature this way too: https://github.com/search?q=repo%3Allvm%2Fllvm-project%20verifyInstructionPredicates&type=code So yeah as @tlively said we seem to follow the pattern that other targets are using. Not sure whether there is another mechanical way to do this kind of checks within AsmPrinter. |
We can probably check things like this in the backend as well, but not sure if it is worth duplicating the effort, given that EH may not be only thing to be checked this way. llvm-project/clang/lib/Driver/ToolChains/WebAssembly.cpp Lines 331 to 369 in d0d05ae
Also this will fail in |
I'm not sure how it works, but the point of comparison for other backends for me is: target triple = "x86_64-unknown-linux-gnu"
define <8 x i16> @test(<8 x i16> %a) #0 {
start:
%b = tail call <8 x i16> @llvm.x86.sse41.phminposuw(<8 x i16> %a)
ret <8 x i16> %b
}
declare <8 x i16> @llvm.x86.sse41.phminposuw(<8 x i16>)
attributes #0 = { "target-features"="+sse4.2" } Here only SSE4.2 is enabled but an SSE4.1 intrinsic is used and the backend doesn't fail with this. |
Even though in `Subtarget` we defined `SIMDLevel` as a number so `hasRelaxedSIMD` automatically means `hasSIMD128`, https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L36-L40 https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L107 specifying only `relaxed-simd` feature on a program that needs `simd128` instructions to compile fails, because of this query in `AsmPrinter`: https://github.com/llvm/llvm-project/blob/d0d05aec3b6792136a9f75eb85dd2ea66005ae12/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp#L644-L645 This `verifyInstructionPredicates` function (and other functions called by this function) is generated by https://github.com/llvm/llvm-project/blob/main/llvm/utils/TableGen/InstrInfoEmitter.cpp, and looks like this (you can check it in the `lib/Target/WebAssembly/WebAssemblyGenInstrInfo.inc` in your build directory): ```cpp void verifyInstructionPredicates( unsigned Opcode, const FeatureBitset &Features) { FeatureBitset AvailableFeatures = computeAvailableFeatures(Features); FeatureBitset RequiredFeatures = computeRequiredFeatures(Opcode); FeatureBitset MissingFeatures = (AvailableFeatures & RequiredFeatures) ^ RequiredFeatures; ... } ``` And `computeAvailableFeatures` there is just a set query, like this: ```cpp inline FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) { FeatureBitset Features; if (FB[WebAssembly::FeatureAtomics]) Features.set(Feature_HasAtomicsBit); if (FB[WebAssembly::FeatureBulkMemory]) Features.set(Feature_HasBulkMemoryBit); if (FB[WebAssembly::FeatureExceptionHandling]) Features.set(Feature_HasExceptionHandlingBit); ... ``` So this is how currently `HasSIMD128` is defined: https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td#L79-L81 The things being checked in this `computeAvailableFeatures`, and in turn in `AsmPrinter`, are `AssemblerPredicate`s. These only check which bits are set in the features set and are different from `Predicate`s, which can call `Subtarget` functions like `Subtarget->hasSIMD128()`. But apparently we can use `all_of` and `any_of` directives in `AssemblerPredicate`, and we can make `simd128`'s `AssemblerPredicate` set in `relaxed-simd` is set by the condition as an 'or' of the two. Fixes llvm#98502.
X86 has that verification commented out: llvm-project/llvm/lib/Target/X86/X86MCInstLower.cpp Lines 2188 to 2190 in 0caf0c9
But this is not the reason your SSE test works. Even if I revive this line your test still works fine. The reason is what llvm-project/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td Lines 79 to 81 in 0caf0c9
but while X86 has many features (= llvm-project/llvm/lib/Target/X86/X86InstrPredicates.td Lines 187 to 198 in 0caf0c9
So its computeAvailableFeature in X86GenInstrInfo.inc is very simple and it doesn't check things like SSE:
inline FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) {
FeatureBitset Features;
if (!FB[X86::Is64Bit])
Features.set(Feature_Not64BitModeBit);
if (FB[X86::Is64Bit])
Features.set(Feature_In64BitModeBit);
if (FB[X86::Is16Bit])
Features.set(Feature_In16BitModeBit);
if (!FB[X86::Is16Bit])
Features.set(Feature_Not16BitModeBit);
if (FB[X86::Is32Bit])
Features.set(Feature_In32BitModeBit);
return Features;
} Anyway, for Wasm I don't think we necessarily need to make |
Aha makes sense, thanks for digging through that! Additionally thanks for #99803, it's much appreciated! |
…99803) Even though in `Subtarget` we defined `SIMDLevel` as a number so `hasRelaxedSIMD` automatically means `hasSIMD128`, https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L36-L40 https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L107 specifying only `relaxed-simd` feature on a program that needs `simd128` instructions to compile fails, because of this query in `AsmPrinter`: https://github.com/llvm/llvm-project/blob/d0d05aec3b6792136a9f75eb85dd2ea66005ae12/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp#L644-L645 This `verifyInstructionPredicates` function (and other functions called by this function) is generated by https://github.com/llvm/llvm-project/blob/main/llvm/utils/TableGen/InstrInfoEmitter.cpp, and looks like this (you can check it in the `lib/Target/WebAssembly/WebAssemblyGenInstrInfo.inc` in your build directory): ```cpp void verifyInstructionPredicates( unsigned Opcode, const FeatureBitset &Features) { FeatureBitset AvailableFeatures = computeAvailableFeatures(Features); FeatureBitset RequiredFeatures = computeRequiredFeatures(Opcode); FeatureBitset MissingFeatures = (AvailableFeatures & RequiredFeatures) ^ RequiredFeatures; ... } ``` And `computeAvailableFeatures` is just a set query, like this: ```cpp inline FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) { FeatureBitset Features; if (FB[WebAssembly::FeatureAtomics]) Features.set(Feature_HasAtomicsBit); if (FB[WebAssembly::FeatureBulkMemory]) Features.set(Feature_HasBulkMemoryBit); if (FB[WebAssembly::FeatureExceptionHandling]) Features.set(Feature_HasExceptionHandlingBit); ... ``` So this is how currently `HasSIMD128` is defined: https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td#L79-L81 The things being checked in this `computeAvailableFeatures`, and in turn in `AsmPrinter`, are `AssemblerPredicate`s. These only check which bits are set in the features set and are different from `Predicate`s, which can call `Subtarget` functions like `Subtarget->hasSIMD128()`. But apparently we can use `all_of` and `any_of` directives in `AssemblerPredicate`, and we can make `simd128`'s `AssemblerPredicate` set in `relaxed-simd` is set by the condition as an 'or' of the two. Fixes #98502.
…99803) Summary: Even though in `Subtarget` we defined `SIMDLevel` as a number so `hasRelaxedSIMD` automatically means `hasSIMD128`, https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L36-L40 https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblySubtarget.h#L107 specifying only `relaxed-simd` feature on a program that needs `simd128` instructions to compile fails, because of this query in `AsmPrinter`: https://github.com/llvm/llvm-project/blob/d0d05aec3b6792136a9f75eb85dd2ea66005ae12/llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp#L644-L645 This `verifyInstructionPredicates` function (and other functions called by this function) is generated by https://github.com/llvm/llvm-project/blob/main/llvm/utils/TableGen/InstrInfoEmitter.cpp, and looks like this (you can check it in the `lib/Target/WebAssembly/WebAssemblyGenInstrInfo.inc` in your build directory): ```cpp void verifyInstructionPredicates( unsigned Opcode, const FeatureBitset &Features) { FeatureBitset AvailableFeatures = computeAvailableFeatures(Features); FeatureBitset RequiredFeatures = computeRequiredFeatures(Opcode); FeatureBitset MissingFeatures = (AvailableFeatures & RequiredFeatures) ^ RequiredFeatures; ... } ``` And `computeAvailableFeatures` is just a set query, like this: ```cpp inline FeatureBitset computeAvailableFeatures(const FeatureBitset &FB) { FeatureBitset Features; if (FB[WebAssembly::FeatureAtomics]) Features.set(Feature_HasAtomicsBit); if (FB[WebAssembly::FeatureBulkMemory]) Features.set(Feature_HasBulkMemoryBit); if (FB[WebAssembly::FeatureExceptionHandling]) Features.set(Feature_HasExceptionHandlingBit); ... ``` So this is how currently `HasSIMD128` is defined: https://github.com/llvm/llvm-project/blob/0caf0c93e759816663af52e8632d1c3953dbc715/llvm/lib/Target/WebAssembly/WebAssemblyInstrInfo.td#L79-L81 The things being checked in this `computeAvailableFeatures`, and in turn in `AsmPrinter`, are `AssemblerPredicate`s. These only check which bits are set in the features set and are different from `Predicate`s, which can call `Subtarget` functions like `Subtarget->hasSIMD128()`. But apparently we can use `all_of` and `any_of` directives in `AssemblerPredicate`, and we can make `simd128`'s `AssemblerPredicate` set in `relaxed-simd` is set by the condition as an 'or' of the two. Fixes #98502. Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250795
Given this input:
this currently crashes with:
This works if
+simd128
is added but given this it seems like this might be intended to work as-is instead of requiring an explicit enabling of the+simd128
feature.The text was updated successfully, but these errors were encountered: