[dart2wasm] Conversion functions for native int and float types

This adds constructors and conversion methods to the native Wasm int and
float types WasmI32, WasmI64, WasmF32 and WasmF64 to convert between
these and Dart int/double. This enables imports and exports to use the
native Wasm types to specify the Wasm function signature concisely.

Change-Id: I3ff459ad9c16574ac6186f753036acc17fa6814d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/234400
Reviewed-by: Joshua Litt <joshualitt@google.com>
Commit-Queue: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart
index d94cd8c..e795d85 100644
--- a/pkg/dart2wasm/lib/intrinsics.dart
+++ b/pkg/dart2wasm/lib/intrinsics.dart
@@ -355,6 +355,40 @@
       }
     }
 
+    // Wasm(I32|I64|F32|F64) conversions
+    if (node.interfaceTarget.enclosingClass?.superclass?.superclass ==
+        translator.wasmTypesBaseClass) {
+      w.StorageType receiverType =
+          translator.builtinTypes[node.interfaceTarget.enclosingClass]!;
+      switch (receiverType) {
+        case w.NumType.i32:
+          assert(name == "toIntSigned" || name == "toIntUnsigned");
+          codeGen.wrap(receiver, w.NumType.i32);
+          switch (name) {
+            case "toIntSigned":
+              b.i64_extend_i32_s();
+              break;
+            case "toIntUnsigned":
+              b.i64_extend_i32_u();
+              break;
+          }
+          return w.NumType.i64;
+        case w.NumType.i64:
+          assert(name == "toInt");
+          codeGen.wrap(receiver, w.NumType.i64);
+          return w.NumType.i64;
+        case w.NumType.f32:
+          assert(name == "toDouble");
+          codeGen.wrap(receiver, w.NumType.f32);
+          b.f64_promote_f32();
+          return w.NumType.f64;
+        case w.NumType.f64:
+          assert(name == "toDouble");
+          codeGen.wrap(receiver, w.NumType.f64);
+          return w.NumType.f64;
+      }
+    }
+
     // List.[] on list constants
     if (receiver is ConstantExpression &&
         receiver.constant is ListConstant &&
@@ -615,6 +649,30 @@
       return w.RefType.def(arrayType, nullable: false);
     }
 
+    // Wasm(I32|I64|F32|F64) constructors
+    if (node.target.enclosingClass?.superclass?.superclass ==
+        translator.wasmTypesBaseClass) {
+      Expression value = node.arguments.positional[0];
+      w.StorageType targetType =
+          translator.builtinTypes[node.target.enclosingClass]!;
+      switch (targetType) {
+        case w.NumType.i32:
+          codeGen.wrap(value, w.NumType.i64);
+          b.i32_wrap_i64();
+          return w.NumType.i32;
+        case w.NumType.i64:
+          codeGen.wrap(value, w.NumType.i64);
+          return w.NumType.i64;
+        case w.NumType.f32:
+          codeGen.wrap(value, w.NumType.f64);
+          b.f32_demote_f64();
+          return w.NumType.f32;
+        case w.NumType.f64:
+          codeGen.wrap(value, w.NumType.f64);
+          return w.NumType.f64;
+      }
+    }
+
     return null;
   }
 
diff --git a/sdk/lib/wasm/wasm_types.dart b/sdk/lib/wasm/wasm_types.dart
index 1ec7bb3..288deaa 100644
--- a/sdk/lib/wasm/wasm_types.dart
+++ b/sdk/lib/wasm/wasm_types.dart
@@ -47,19 +47,32 @@
 
 /// The Wasm `i32` type.
 @pragma("wasm:entry-point")
-class WasmI32 extends _WasmInt {}
+class WasmI32 extends _WasmInt {
+  external factory WasmI32.fromInt(int value);
+  external int toIntSigned();
+  external int toIntUnsigned();
+}
 
 /// The Wasm `i64` type.
 @pragma("wasm:entry-point")
-class WasmI64 extends _WasmInt {}
+class WasmI64 extends _WasmInt {
+  external factory WasmI64.fromInt(int value);
+  external int toInt();
+}
 
 /// The Wasm `f32` type.
 @pragma("wasm:entry-point")
-class WasmF32 extends _WasmFloat {}
+class WasmF32 extends _WasmFloat {
+  external factory WasmF32.fromDouble(double value);
+  external double toDouble();
+}
 
 /// The Wasm `f64` type.
 @pragma("wasm:entry-point")
-class WasmF64 extends _WasmFloat {}
+class WasmF64 extends _WasmFloat {
+  external factory WasmF64.fromDouble(double value);
+  external double toDouble();
+}
 
 /// A Wasm array with integer element type.
 @pragma("wasm:entry-point")
@@ -88,3 +101,13 @@
   external T read(int index);
   external void write(int index, T value);
 }
+
+extension IntToWasmInt on int {
+  WasmI32 toWasmI32() => WasmI32.fromInt(this);
+  WasmI64 toWasmI64() => WasmI64.fromInt(this);
+}
+
+extension DoubleToWasmFloat on double {
+  WasmF32 toWasmF32() => WasmF32.fromDouble(this);
+  WasmF64 toWasmF64() => WasmF64.fromDouble(this);
+}