Repository: halide/Halide (C++)

Issue:
WASM regression with LLVM.
The issue is a splat instruction selection. Got this from the buildbot:

```
i8x16.splat                     (wasm-32-wasmrt-wasm_simd128)
Failed: i8x16.splat did not generate for target=wasm-32-wasmrt-wasm_simd128 vector_width=16. Instead we got:
	.file	"force_include_types.cpp"
	.globaltype	__memory_base, i32, immutable
	.functype	halide_free (i32, i32) -> ()
	.functype	halide_malloc (i32, i32) -> (i32)
	.functype	test_op_i8x16_splat_18 (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) -> (i32)
	.functype	test_op_i8x16_splat_18_argv (i32) -> (i32)
	.functype	test_op_i8x16_splat_18_metadata () -> (i32)
	.section	.text.test_op_i8x16_splat_18,"",@
	.globl	test_op_i8x16_splat_18          # -- Begin function test_op_i8x16_splat_18
	.type	test_op_i8x16_splat_18,@function
test_op_i8x16_splat_18:                 # @test_op_i8x16_splat_18
	.functype	test_op_i8x16_splat_18 (i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32) -> (i32)
	.local  	i32, i32, i32, i32, v128, v128, i32, i32
# %bb.0:                                # %entry
	local.get	5
	i32.load	12
	local.get	5
	i32.load	32
	i32.load	0
	i32.sub 
	local.set	13
	local.get	12
	i32.load	12
	local.set	14
	i32.const	0
	local.set	15
	i32.const	0
	i32.const	98304
	call	halide_malloc
	local.tee	16
	local.set	12
.LBB0_1:                                # %"1_for_op_i8x16_splat_18.s0.y"
                                        # =>This Loop Header: Depth=1
                                        #     Child Loop BB0_2 Depth 2
	loop    	                                # label0:
	i32.const	0
	local.set	5
.LBB0_2:                                # %"2_for_op_i8x16_splat_18.s0.x.x"
                                        #   Parent Loop BB0_1 Depth=1
                                        # =>  This Inner Loop Header: Depth=2
	loop    	                                # label1:
	local.get	12
	local.get	5
	i32.add 
	local.get	13
	local.get	5
	i32.add 
	v128.load	0
	local.tee	17
	v128.const	42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42
	local.tee	18
	i16x8.extmul_low_i8x16_u
	local.get	17
	local.get	18
	i16x8.extmul_high_i8x16_u
	i8x16.shuffle	0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
	v128.store	0
	local.get	5
	i32.const	16
	i32.add 
	local.tee	5
	i32.const	768
	i32.ne  
	br_if   	0                               # 0: up to label1
# %bb.3:                                # %"2_end_for_op_i8x16_splat_18.s0.x.x"
                                        #   in Loop: Header=BB0_1 Depth=1
	end_loop
	local.get	12
	i32.const	768
	i32.add 
	local.set	12
	local.get	15
	i32.const	1
	i32.add 
	local.tee	15
	i32.const	128
	i32.ne  
	br_if   	0                               # 0: up to label0
# %bb.4:                                # %"3_for_maximum$34.s1.r2398$y.preheader"
	end_loop
	i32.const	0
	local.set	12
	local.get	16
	local.set	19
	i32.const	0
	local.set	20
.LBB0_5:                                # %"3_for_maximum$34.s1.r2398$y"
                                        # =>This Loop Header: Depth=1
                                        #     Child Loop BB0_6 Depth 2
	loop    	                                # label2:
	i32.const	0
	local.set	5
.LBB0_6:                                # %"4_for_maximum$34.s1.r2398$x"
                                        #   Parent Loop BB0_5 Depth=1
                                        # =>  This Inner Loop Header: Depth=2
	loop    	                                # label3:
	local.get	19
	local.get	5
	i32.add 
	i32.load8_u	0
	local.get	13
	local.get	5
	i32.add 
	i32.load8_u	0
	i32.const	42
	i32.mul 
	i32.const	254
	i32.and 
	i32.sub 
	local.tee	15
	local.get	15
	i32.const	31
	i32.shr_s
	local.tee	15
	i32.xor 
	local.get	15
	i32.sub 
	local.tee	15
	local.get	12
	i32.const	255
	i32.and 
	local.tee	12
	local.get	15
	local.get	12
	i32.gt_u
	i32.select
	local.set	12
	local.get	5
	i32.const	1
	i32.add 
	local.tee	5
	i32.const	768
	i32.ne  
	br_if   	0                               # 0: up to label3
# %bb.7:                                # %"4_end_for_maximum$34.s1.r2398$x"
                                        #   in Loop: Header=BB0_5 Depth=1
	end_loop
	local.get	19
	i32.const	768
	i32.add 
	local.set	19
	local.get	20
	i32.const	1
	i32.add 
	local.tee	20
	i32.const	128
	i32.ne  
	br_if   	0                               # 0: up to label2
# %bb.8:                                # %call_destructor.exit
	end_loop
	i32.const	0
	local.get	16
	call	halide_free
	local.get	14
	local.get	12
	i32.const	255
	i32.and 
	f64.convert_i32_u
	f64.store	0
	i32.const	0
                                        # fallthrough-return
	end_function
```

For this test:

```cpp
// Create vector with identical lanes
// (Note that later LLVMs will use 64-bit constants for some smaller splats)
check("i8x16.splat", 16 * w, u8_1 * u8(42));
```

So LLVM now generates v128.const, which makes sense to me as the variable actually is a compile-time constant.

I think the solution is to test something like: `u8_1 * u8(in_u8(2) << 1)`.


Discussion: The spam we are seeing is having this backtrace:

```c++
(gdb) bt
#0  0x00007fffee6370c0 in ApplyFeatureFlag(llvm::FeatureBitset&, llvm::StringRef, llvm::ArrayRef<llvm::SubtargetFeatureKV>) [clone .llvm.4520640816656589715] () at /usr/lib64/libLLVM.so.20.1
#1  0x00007fffee6358ee in getFeatures(llvm::MCSubtargetInfo&, llvm::StringRef, llvm::StringRef, llvm::StringRef, llvm::ArrayRef<llvm::StringRef>, llvm::ArrayRef<llvm::SubtargetSubTypeKV>, llvm::ArrayRef<llvm::SubtargetFeatureKV>) () at /usr/lib64/libLLVM.so.20.1
#2  0x00007fffee635385 in llvm::MCSubtargetInfo::InitMCProcessorInfo(llvm::StringRef, llvm::StringRef, llvm::StringRef) () at /usr/lib64/libLLVM.so.20.1
#3  0x00007fffee636801 in llvm::MCSubtargetInfo::MCSubtargetInfo(llvm::Triple const&, llvm::StringRef, llvm::StringRef, llvm::StringRef, llvm::ArrayRef<llvm::StringRef>, llvm::ArrayRef<llvm::SubtargetFeatureKV>, llvm::ArrayRef<llvm::SubtargetSubTypeKV>, llvm::MCWriteProcResEntry const*, llvm::MCWriteLatencyEntry const*, llvm::MCReadAdvanceEntry const*, llvm::InstrStage const*, unsigned int const*, unsigned int const*) () at /usr/lib64/libLLVM.so.20.1
#4  0x00007ffff03afea1 in llvm::WebAssemblySubtarget::WebAssemblySubtarget(llvm::Triple const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, llvm::TargetMachine const&) () at /usr/lib64/libLLVM.so.20.1
#5  0x00007ffff03b1537 in llvm::WebAssemblyTargetMachine::getSubtargetImpl(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) const () at /usr/lib64/libLLVM.so.20.1
#6  0x00007ffff03b1aea in llvm::WebAssemblyTargetMachine::getSubtargetImpl(llvm::Function const&) const () at /usr/lib64/libLLVM.so.20.1
#7  0x00007ffff03b1d44 in llvm::WebAssemblyTargetMachine::getTargetTransformInfo(llvm::Function const&) const () at /usr/lib64/libLLVM.so.20.1
#9  0x00007fffee518f51 in llvm::TargetTransformInfoWrapperPass::getTTI(llvm::Function const&) () at /usr/lib64/libLLVM.so.20.1
#10 0x00007fffee27110a in llvm::AssumptionCacheTracker::getAssumptionCache(llvm::Function&) () at /usr/lib64/libLLVM.so.20.1
#11 0x00007fffed758b6e in llvm::InlineFunction(llvm::CallBase&, llvm::InlineFunctionInfo&, bool, llvm::AAResults*, bool, llvm::Function*) () at /usr/lib64/libLLVM.so.20.1
#12 0x00007fffedd94c1e in (anonymous namespace)::AlwaysInlineImpl(llvm::Module&, bool, llvm::ProfileSummaryInfo&, llvm::AnalysisManager<llvm::Function>*, llvm::function_ref<llvm::AssumptionCache& (llvm::Function&)>, llvm::function_ref<llvm::AAResults& (llvm::Function&)>) [clone .llvm.14359792980074514065] () at /usr/lib64/libLLVM.so.20.1
#13 0x00007fffedd961de in (anonymous namespace)::AlwaysInlinerLegacyPass::runOnModule(llvm::Module&) [clone .llvm.14359792980074514065] () at /usr/lib64/libLLVM.so.20.1
#14 0x00007fffec9d8c6f in llvm::legacy::PassManagerImpl::run(llvm::Module&) () at /usr/lib64/libLLVM.so.20.1
#15 0x00007ffff5c74f30 in Halide::(anonymous namespace)::emit_file (module_in=..., out=..., file_type=llvm::CodeGenFileType::ObjectFile) at /home/martijn/zec/3rd/halide-quick-fix/src/LLVM_Output.cpp:406
#16 0x00007ffff5c75119 in Halide::compile_llvm_module_to_object (module=..., out=...) at /home/martijn/zec/3rd/halide-quick-fix/src/LLVM_Output.cpp:426
#17 0x00007ffff664a775 in Halide::Internal::(anonymous namespace)::compile_to_wasm (module=..., fn_name="error_op_f32_trunc_5") at /home/martijn/zec/3rd/halide-quick-fix/src/WasmExecutor.cpp:305
#18 0x00007ffff6650c7a in Halide::Internal::WasmModuleContents::WasmModuleContents (this=0x7fffd00e5cd0, halide_module=..., arguments=std::vector of length 14, capacity 14 = {...}, fn_name="error_op_f32_trunc_5", jit_externs=std::map with 0 elements, extern_deps=std::vector of length 0, capacity 0) at /home/martijn/zec/3rd/halide-quick-fix/src/WasmExecutor.cpp:2289
#19 0x00007ffff6652eea in Halide::Internal::WasmModule::compile (module=..., arguments=std::vector of length 14, capacity 14 = {...}, fn_name="error_op_f32_trunc_5", jit_externs=std::map with 0 elements, extern_deps=std::vector of length 0, capacity 0) at /home/martijn/zec/3rd/halide-quick-fix/src/WasmExecutor.cpp:2696
#20 0x00007ffff5d28cd5 in Halide::Pipeline::compile_jit_cache (module=..., args=std::vector of length 14, capacity 14 = {...}, outputs=std::vector of length 1, capacity 1 = {...}, jit_externs_in=std::map with 0 elements, target_arg=...) at /home/martijn/zec/3rd/halide-quick-fix/src/Pipeline.cpp:712
#21 0x00007ffff5d28422 in Halide::Pipeline::compile_to_callable (this=0x7fffec4a11e8, args_in=std::vector of length 12, capacity 12 = {...}, target_arg=...) at /home/martijn/zec/3rd/halide-quick-fix/src/Pipeline.cpp:661
#22 0x00007ffff5ac0d58 in Halide::Func::compile_to_callable (this=0x7fffec4a29a0, args=std::vector of length 12, capacity 12 = {...}, target=...) at /home/martijn/zec/3rd/halide-quick-fix/src/Func.cpp:3617
#23 0x000000000041f045 in Halide::SimdOpCheckTest::check_one (this=0x7fffffffd6b0, op="f32.trunc", name="op_f32_trunc_5", vector_width=1, e=...) at /home/martijn/zec/3rd/halide-quick-fix/test/correctness/simd_op_check.h:365
```
Figured it out!! https://github.com/halide/Halide/pull/8705/files




Fix (patch):
diff --git a/src/LLVM_Runtime_Linker.cpp b/src/LLVM_Runtime_Linker.cpp
index 11dadd9e16fd..b78f525a9033 100644
--- a/src/LLVM_Runtime_Linker.cpp
+++ b/src/LLVM_Runtime_Linker.cpp
@@ -890,6 +890,13 @@ std::unique_ptr<llvm::Module> link_with_wasm_jit_runtime(llvm::LLVMContext *c, c
     constexpr bool allow_stripping_all_weak_functions = true;
     link_modules(modules, t, allow_stripping_all_weak_functions);
 
+    // The initmods are compiled by clang and have some attributes associated with
+    // them that are irrelevant to Wasm. Strip them out.
+    for (llvm::Function &F : *modules[0]) {
+        F.removeFnAttr("target-features");
+        F.removeFnAttr("target-cpu");
+    }
+
     return std::move(modules[0]);
 }