aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2025-04-05 23:26:25 +0900
committernsfisis <nsfisis@gmail.com>2025-04-06 02:03:32 +0900
commit3d0f0b06ee25571034a13a43d2ca660ef687afa9 (patch)
tree9d82454afbd8d26d41180db8718c863f9225e83f
parent24d00a02f51138f1191c8b1f72ccdb14fc622b11 (diff)
downloadphp-waddiwasi-3d0f0b06ee25571034a13a43d2ca660ef687afa9.tar.gz
php-waddiwasi-3d0f0b06ee25571034a13a43d2ca660ef687afa9.tar.zst
php-waddiwasi-3d0f0b06ee25571034a13a43d2ca660ef687afa9.zip
fix: pass ConversionsTest, FloatExprsTest, FloatLiteralsTest
-rw-r--r--BUGS3
-rw-r--r--TODO1
-rw-r--r--phpunit.xml3
-rw-r--r--src/WebAssembly/BinaryFormat/Decoder.php18
-rw-r--r--src/WebAssembly/Execution/NumericOps.php30
-rw-r--r--tests/src/SpecTestsuites/SpecTestsuiteBase.php14
6 files changed, 57 insertions, 12 deletions
diff --git a/BUGS b/BUGS
index 905a6e5..042151c 100644
--- a/BUGS
+++ b/BUGS
@@ -2,9 +2,6 @@
## Numeric
-* ConversionsTest
-* FloatExprsTest
-* FloatLiteralsTest
* FloatMemoryTest
## Validation
diff --git a/TODO b/TODO
index 80e15c0..a361e26 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,6 @@
* Support text format (.wat)
* Implement validation
* Fix known bugs (BUGS)
- * We need to implement software-defined f32 in order to support floating-point number operations.
* Implement NaN propagation
* Provide sane bindings to PHP
* Write PHPDoc for public APIs
diff --git a/phpunit.xml b/phpunit.xml
index 336683b..02e668c 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -28,6 +28,7 @@
<file>tests/src/SpecTestsuites/Core/CallIndirectTest.php</file>
<file>tests/src/SpecTestsuites/Core/CallTest.php</file>
<file>tests/src/SpecTestsuites/Core/ConstTest.php</file>
+ <file>tests/src/SpecTestsuites/Core/ConversionsTest.php</file>
<file>tests/src/SpecTestsuites/Core/DataTest.php</file>
<file>tests/src/SpecTestsuites/Core/ElemTest.php</file>
<file>tests/src/SpecTestsuites/Core/EndiannessTest.php</file>
@@ -39,6 +40,8 @@
<file>tests/src/SpecTestsuites/Core/F64CmpTest.php</file>
<file>tests/src/SpecTestsuites/Core/F64Test.php</file>
<file>tests/src/SpecTestsuites/Core/FacTest.php</file>
+ <file>tests/src/SpecTestsuites/Core/FloatExprsTest.php</file>
+ <file>tests/src/SpecTestsuites/Core/FloatLiteralsTest.php</file>
<file>tests/src/SpecTestsuites/Core/FloatMiscTest.php</file>
<file>tests/src/SpecTestsuites/Core/ForwardTest.php</file>
<file>tests/src/SpecTestsuites/Core/FuncPtrsTest.php</file>
diff --git a/src/WebAssembly/BinaryFormat/Decoder.php b/src/WebAssembly/BinaryFormat/Decoder.php
index e64fac8..cce3c5c 100644
--- a/src/WebAssembly/BinaryFormat/Decoder.php
+++ b/src/WebAssembly/BinaryFormat/Decoder.php
@@ -1091,12 +1091,24 @@ final class Decoder
private function decodeF32(): float
{
$buf = $this->stream->read(4);
- $result = unpack('g', $buf);
+ $result = unpack('V', $buf);
if ($result === false) {
throw new InvalidBinaryFormatException("f32");
}
- assert(isset($result[1]) && is_float($result[1]));
- return $result[1];
+ assert(isset($result[1]) && is_int($result[1]));
+ $i = $result[1];
+ if (($i & 0b01111111100000000000000000000000) === 0b01111111100000000000000000000000) {
+ $sign = ($i & 0b10000000000000000000000000000000) === 0 ? 1 : -1;
+ $payload = $i & 0b00000000011111111111111111111111;
+ $j = ($sign === 1 ? 0 : PHP_INT_MIN) | 0b0111111111110000000000000000000000000000000000000000000000000000 | ($payload << (52 - 23));
+ $result = unpack('d', pack('q', $j));
+ assert(isset($result[1]) && is_float($result[1]));
+ return $result[1];
+ } else {
+ $result = unpack('g', $buf);
+ assert(isset($result[1]) && is_float($result[1]));
+ return $result[1];
+ }
}
/**
diff --git a/src/WebAssembly/Execution/NumericOps.php b/src/WebAssembly/Execution/NumericOps.php
index a24791f..8c061bd 100644
--- a/src/WebAssembly/Execution/NumericOps.php
+++ b/src/WebAssembly/Execution/NumericOps.php
@@ -168,6 +168,7 @@ final readonly class NumericOps
public static function f32Neg(float $x): float
{
+ self::reinterpretF32AsI32($x);
if (is_nan($x)) {
// Negate operator does not work for NaN in PHP.
return self::constructNan(-self::getFloatSign($x), $x);
@@ -341,7 +342,11 @@ final readonly class NumericOps
public static function f64PromoteF32(float $x): float
{
- return $x;
+ if (is_nan($x)) {
+ return NAN;
+ } else {
+ return $x;
+ }
}
public static function f64ReinterpretI32(int $x): float
@@ -1144,7 +1149,15 @@ final readonly class NumericOps
public static function reinterpretI32AsF32(int $x): float
{
- return self::deserializeF32FromBytes(self::serializeI32ToBytes($x));
+ $y = self::convertS32ToU32($x);
+ if (($y & 0b01111111100000000000000000000000) === 0b01111111100000000000000000000000) {
+ $sign = ($y & 0b10000000000000000000000000000000) === 0 ? 1 : -1;
+ $payload = $y & 0b00000000011111111111111111111111;
+ $i = ($sign === 1 ? 0 : PHP_INT_MIN) | 0b0111111111110000000000000000000000000000000000000000000000000000 | ($payload << (52 - 23));
+ return self::reinterpretI64AsF64($i);
+ } else {
+ return self::deserializeF32FromBytes(self::serializeI32ToBytes($x));
+ }
}
public static function reinterpretI64AsF32(int $x): float
@@ -1164,7 +1177,16 @@ final readonly class NumericOps
public static function reinterpretF32AsI32(float $x): int
{
- return self::deserializeI32FromBytes(self::serializeF32ToBytes($x));
+ if (is_nan($x)) {
+ [$sign, , $payload] = self::destructFloat($x);
+ $i = 0
+ | ($sign === 0 ? 0 : 0b10000000000000000000000000000000)
+ | 0b01111111100000000000000000000000
+ | ($payload >> (52 - 23));
+ return self::convertU32ToS32($i);
+ } else {
+ return self::deserializeI32FromBytes(self::serializeF32ToBytes($x));
+ }
}
public static function reinterpretF64AsI32(float $x): int
@@ -1319,7 +1341,7 @@ final readonly class NumericOps
*/
private static function constructNan(int $sign, float $x): float
{
- [$_, $_, $payload] = self::destructFloat($x);
+ [, , $payload] = self::destructFloat($x);
$i = ($sign === 1 ? 0 : PHP_INT_MIN) | 0b01111111_11110000_00000000_00000000_00000000_00000000_00000000_00000000 | $payload;
return self::reinterpretI64AsF64($i);
}
diff --git a/tests/src/SpecTestsuites/SpecTestsuiteBase.php b/tests/src/SpecTestsuites/SpecTestsuiteBase.php
index 6d92ed8..41afbf8 100644
--- a/tests/src/SpecTestsuites/SpecTestsuiteBase.php
+++ b/tests/src/SpecTestsuites/SpecTestsuiteBase.php
@@ -258,7 +258,7 @@ abstract class SpecTestsuiteBase extends TestCase
'i64' => unpack('q', self::convertInt64ToBinary($value))[1],
'f32' => match ($value) {
'nan:canonical', 'nan:arithmetic' => $value,
- default => unpack('g', pack('V', (int)$value))[1],
+ default => self::i32ToF32((int)$value),
},
'f64' => match ($value) {
'nan:canonical', 'nan:arithmetic' => $value,
@@ -270,6 +270,18 @@ abstract class SpecTestsuiteBase extends TestCase
};
}
+ private static function i32ToF32(int $x): float
+ {
+ if (($x & 0b01111111100000000000000000000000) === 0b01111111100000000000000000000000) {
+ $sign = ($x & 0b10000000000000000000000000000000) === 0 ? 1 : -1;
+ $payload = $x & 0b00000000011111111111111111111111;
+ $i = ($sign === 1 ? 0 : PHP_INT_MIN) | 0b0111111111110000000000000000000000000000000000000000000000000000 | ($payload << (52 - 23));
+ return unpack('e', pack('q', $i))[1];
+ } else {
+ return unpack('g', pack('V', $x))[1];
+ }
+ }
+
private function doAction(
array $action,
): array {