[bazel] Add support for enabling relaxed SIMD in wasm_cc_binary (#1728)

It would probably be more elegant to reuse the existing `simd` attribute
and change it's type from boolean into string enum (similar like it's
for `threads`), but that would be a breaking change so I decided to add
additional boolean acknowlidging that `simd=False`, `relaxed_simd=True`
makes no sense and will also enable `simd` as `relaxed_simd` implies
`simd`.
diff --git a/bazel/emscripten_toolchain/toolchain.bzl b/bazel/emscripten_toolchain/toolchain.bzl
index 4c11970..d447f07 100644
--- a/bazel/emscripten_toolchain/toolchain.bzl
+++ b/bazel/emscripten_toolchain/toolchain.bzl
@@ -440,13 +440,14 @@
             name = "do_not_split_linking_cmdline",
         ),
 
-        # Adds simd support, only available with the llvm backend.
+        # Adds simd support
         feature(
             name = "wasm_simd",
         ),
-        # Adds relaxed-simd support, only available with the llvm backend.
+        # Adds relaxed-simd support
         feature(
             name = "wasm_relaxed_simd",
+            implies = ["wasm_simd"],
         ),
         feature(
             name = "precise_long_double_printf",
diff --git a/bazel/emscripten_toolchain/wasm_cc_binary.bzl b/bazel/emscripten_toolchain/wasm_cc_binary.bzl
index da84e64..d0f21b1 100644
--- a/bazel/emscripten_toolchain/wasm_cc_binary.bzl
+++ b/bazel/emscripten_toolchain/wasm_cc_binary.bzl
@@ -17,8 +17,10 @@
     if attr.exit_runtime == True:
         features.append("exit_runtime")
 
-    if attr.simd:
+    if attr.simd != "off":
         features.append("wasm_simd")
+        if attr.simd == "relaxed_simd":
+            features.append("wasm_relaxed_simd")
 
     platform = "@emsdk//:platform_wasm"
     if attr.standalone:
@@ -78,8 +80,10 @@
         default = "_default",
         values = ["_default", "emscripten", "off"],
     ),
-    "simd": attr.bool(
-        default = False,
+    "simd": attr.string(
+        default = "off",
+        values = ["off", "simd128", "relaxed_simd"],
+        doc = "Enable SIMD support via emscripten.",
     ),
     "standalone": attr.bool(
         default = False,
diff --git a/bazel/hello-world/BUILD b/bazel/hello-world/BUILD
index 07fbed5..061f4c1 100644
--- a/bazel/hello-world/BUILD
+++ b/bazel/hello-world/BUILD
@@ -19,5 +19,11 @@
 wasm_cc_binary(
     name = "hello-world-wasm-simd",
     cc_target = ":hello-world-simd",
-    simd = True,
+    simd = "simd128",
+)
+
+wasm_cc_binary(
+    name = "hello-world-wasm-relaxed-simd",
+    cc_target = ":hello-world-simd",
+    simd = "relaxed_simd",
 )
diff --git a/bazel/hello-world/hello-world-simd.cc b/bazel/hello-world/hello-world-simd.cc
index 649adab..c876cc4 100644
--- a/bazel/hello-world/hello-world-simd.cc
+++ b/bazel/hello-world/hello-world-simd.cc
@@ -8,3 +8,42 @@
     wasm_v128_store(&out[i], prod);
   }
 }
+
+#ifdef __wasm_relaxed_simd__
+
+float dot_product_accumulate(const float* A, const float* B, int len, float initial_sum) {
+    int i = 0;
+
+    // Initialize accumulator vector with the initial sum spread across all lanes
+    // We will sum these lanes at the end.
+    v128_t acc = wasm_f32x4_splat(initial_sum);
+
+    // Main loop: process 4 elements at a time
+    // Requires len to be at least 4. If len < 4, the loop is skipped.
+    for (; i + 4 <= len; i += 4) {
+        // Load 4 floats from A and B
+        v128_t va = wasm_v128_load(&A[i]);
+        v128_t vb = wasm_v128_load(&B[i]);
+
+        // Fused Multiply-Add: acc = (va * vb) + acc
+        // Uses relaxed rounding for performance
+        acc = __builtin_wasm_relaxed_madd_f32x4(va, vb, acc);
+    }
+
+    // Horizontal add: sum the 4 lanes of the accumulator
+    // acc = {s0, s1, s2, s3}
+    // sum = s0 + s1 + s2 + s3
+    v128_t sum1 = wasm_f32x4_add(acc, wasm_i32x4_shuffle(acc, acc, 1, 0, 3, 2)); // {s0+s1, s0+s1, s2+s3, s2+s3}
+    v128_t sum2 = wasm_f32x4_add(sum1, wasm_i32x4_shuffle(sum1, sum1, 2, 3, 0, 1)); // {total, total, total, total}
+
+    float result = wasm_f32x4_extract_lane(sum2, 0);
+
+    // Handle remaining elements (tail loop)
+    for (; i < len; i++) {
+        result += A[i] * B[i];
+    }
+
+    return result;
+}
+
+#endif
diff --git a/test/test_bazel.ps1 b/test/test_bazel.ps1
index 143c23c..dc0393a 100644
--- a/test/test_bazel.ps1
+++ b/test/test_bazel.ps1
@@ -8,6 +8,9 @@
 bazel build //hello-world:hello-world-wasm-simd
 if (-not $?) { Exit $LastExitCode }
 
+bazel build //hello-world:hello-world-wasm-relaxed-simd
+if (-not $?) { Exit $LastExitCode }
+
 Set-Location test_external
 
 bazel build //:hello-world-wasm
diff --git a/test/test_bazel.sh b/test/test_bazel.sh
index b2a198e..f4699fe 100755
--- a/test/test_bazel.sh
+++ b/test/test_bazel.sh
@@ -22,6 +22,7 @@
 
 bazel build //hello-world:hello-world-wasm
 bazel build //hello-world:hello-world-wasm-simd
+bazel build //hello-world:hello-world-wasm-relaxed-simd
 
 pushd test_external
 bazel build //:hello-world-wasm
diff --git a/test/test_bazel_mac.sh b/test/test_bazel_mac.sh
index 8be9f90..26a31c8 100755
--- a/test/test_bazel_mac.sh
+++ b/test/test_bazel_mac.sh
@@ -21,6 +21,7 @@
 cd bazel
 bazel build //hello-world:hello-world-wasm
 bazel build //hello-world:hello-world-wasm-simd
+bazel build //hello-world:hello-world-wasm-relaxed-simd
 
 cd test_external
 bazel build //long_command_line:long_command_line_wasm