Skip to content

Commit 95990c4

Browse files
alexcrichtonkripken
authored andcommitted
wasm2asm: Implement f32/f64.copysign (WebAssembly#1551)
This commit implements the `copysign` instruction for the wasm2asm binary. The implementation here is a new pass which wholesale replaces `copysign` instructions with the equivalent bit ops and reinterpretation instructions. It's intended that this matches Emscripten's implementation of lowering here.
1 parent 4615f00 commit 95990c4

10 files changed

Lines changed: 193 additions & 0 deletions

File tree

build-js.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ echo "building shared bitcode"
110110
$BINARYEN_SRC/passes/PrintCallGraph.cpp \
111111
$BINARYEN_SRC/passes/RedundantSetElimination.cpp \
112112
$BINARYEN_SRC/passes/RelooperJumpThreading.cpp \
113+
$BINARYEN_SRC/passes/RemoveCopysign.cpp \
113114
$BINARYEN_SRC/passes/RemoveImports.cpp \
114115
$BINARYEN_SRC/passes/RemoveMemory.cpp \
115116
$BINARYEN_SRC/passes/RemoveUnusedBrs.cpp \

src/passes/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ SET(passes_SOURCES
3030
RedundantSetElimination.cpp
3131
RelooperJumpThreading.cpp
3232
ReReloop.cpp
33+
RemoveCopysign.cpp
3334
RemoveImports.cpp
3435
RemoveMemory.cpp
3536
RemoveUnusedBrs.cpp

src/passes/RemoveCopysign.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
* Copyright 2018 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
//
18+
// Removes the `f32.copysign` and `f64.copysign` instructions and replaces them
19+
// with equivalent bit operations. Primarily intended to be used with `wasm2asm`
20+
// where `Math.copysign` doesn't exist.
21+
//
22+
23+
#include <wasm.h>
24+
#include <pass.h>
25+
26+
#include "wasm-builder.h"
27+
28+
namespace wasm {
29+
30+
struct RemoveCopysignPass : public WalkerPass<PostWalker<RemoveCopysignPass>> {
31+
bool isFunctionParallel() override { return false; }
32+
33+
Pass* create() override { return new RemoveCopysignPass; }
34+
35+
void doWalkModule(Module* module) {
36+
if (!builder) builder = make_unique<Builder>(*module);
37+
PostWalker<RemoveCopysignPass>::doWalkModule(module);
38+
}
39+
40+
void doWalkFunction(Function* func) {
41+
if (!builder) builder = make_unique<Builder>(*getModule());
42+
PostWalker<RemoveCopysignPass>::doWalkFunction(func);
43+
}
44+
45+
void visitBinary(Binary *curr) {
46+
Literal signBit, otherBits;
47+
UnaryOp int2float, float2int;
48+
BinaryOp bitAnd, bitOr;
49+
switch (curr->op) {
50+
case CopySignFloat32:
51+
float2int = ReinterpretFloat32;
52+
int2float = ReinterpretInt32;
53+
bitAnd = AndInt32;
54+
bitOr = OrInt32;
55+
signBit = Literal(uint32_t(1 << 31));
56+
otherBits = Literal(uint32_t(1 << 31) - 1);
57+
break;
58+
59+
case CopySignFloat64:
60+
float2int = ReinterpretFloat64;
61+
int2float = ReinterpretInt64;
62+
bitAnd = AndInt64;
63+
bitOr = OrInt64;
64+
signBit = Literal(uint64_t(1) << 63);
65+
otherBits = Literal((uint64_t(1) << 63) - 1);
66+
break;
67+
68+
default: return;
69+
}
70+
71+
replaceCurrent(
72+
builder->makeUnary(
73+
int2float,
74+
builder->makeBinary(
75+
bitOr,
76+
builder->makeBinary(
77+
bitAnd,
78+
builder->makeUnary(
79+
float2int,
80+
curr->left
81+
),
82+
builder->makeConst(otherBits)
83+
),
84+
builder->makeBinary(
85+
bitAnd,
86+
builder->makeUnary(
87+
float2int,
88+
curr->right
89+
),
90+
builder->makeConst(signBit)
91+
)
92+
)
93+
)
94+
);
95+
}
96+
97+
private:
98+
std::unique_ptr<Builder> builder;
99+
};
100+
101+
Pass *createRemoveCopysignPass() {
102+
return new RemoveCopysignPass();
103+
}
104+
105+
} // namespace wasm
106+

src/passes/pass.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ void PassRegistry::registerPasses() {
9999
registerPass("print-full", "print in full s-expression format", createFullPrinterPass);
100100
registerPass("print-call-graph", "print call graph", createPrintCallGraphPass);
101101
registerPass("relooper-jump-threading", "thread relooper jumps (fastcomp output only)", createRelooperJumpThreadingPass);
102+
registerPass("remove-copysign", "removes the copysign instruction", createRemoveCopysignPass);
102103
registerPass("remove-imports", "removes imports and replaces them with nops", createRemoveImportsPass);
103104
registerPass("remove-memory", "removes memory segments", createRemoveMemoryPass);
104105
registerPass("remove-unused-brs", "removes breaks from locations that are not needed", createRemoveUnusedBrsPass);

src/passes/passes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Pass* createPrecomputePropagatePass();
5656
Pass* createPrinterPass();
5757
Pass* createPrintCallGraphPass();
5858
Pass* createRelooperJumpThreadingPass();
59+
Pass* createRemoveCopysignPass();
5960
Pass* createRemoveImportsPass();
6061
Pass* createRemoveMemoryPass();
6162
Pass* createRemoveUnusedBrsPass();

src/wasm2asm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
384384
addWasmCompatibilityFuncs(wasm);
385385
PassRunner runner(wasm);
386386
runner.add<AutoDrop>();
387+
runner.add("remove-copysign"); // must be before i64-to-i32
387388
runner.add("i64-to-i32-lowering");
388389
runner.add("flatten");
389390
runner.add("simplify-locals-notee-nostructure");

test/passes/remove-copysign.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(module
2+
(type $0 (func (param f64 f64) (result f64)))
3+
(type $1 (func (param f32 f32) (result f32)))
4+
(func $copysign64 (; 0 ;) (type $0) (param $0 f64) (param $1 f64) (result f64)
5+
(f64.reinterpret/i64
6+
(i64.or
7+
(i64.and
8+
(i64.reinterpret/f64
9+
(get_local $0)
10+
)
11+
(i64.const 9223372036854775807)
12+
)
13+
(i64.and
14+
(i64.reinterpret/f64
15+
(get_local $1)
16+
)
17+
(i64.const -9223372036854775808)
18+
)
19+
)
20+
)
21+
)
22+
(func $copysign32 (; 1 ;) (type $1) (param $0 f32) (param $1 f32) (result f32)
23+
(f32.reinterpret/i32
24+
(i32.or
25+
(i32.and
26+
(i32.reinterpret/f32
27+
(get_local $0)
28+
)
29+
(i32.const 2147483647)
30+
)
31+
(i32.and
32+
(i32.reinterpret/f32
33+
(get_local $1)
34+
)
35+
(i32.const -2147483648)
36+
)
37+
)
38+
)
39+
)
40+
)

test/passes/remove-copysign.wast

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(module
2+
(func $copysign64 (param $0 f64) (param $1 f64) (result f64)
3+
(f64.copysign (get_local $0) (get_local $1)))
4+
(func $copysign32 (param $0 f32) (param $1 f32) (result f32)
5+
(f32.copysign (get_local $0) (get_local $1)))
6+
)
7+

test/wasm2asm/float-ops.2asm.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,35 @@ function asmFunc(global, env, buffer) {
206206
return +Math_sqrt($0);
207207
}
208208

209+
function copysign64($0, $1) {
210+
$0 = +$0;
211+
$1 = +$1;
212+
var i64toi32_i32$0 = 0, i64toi32_i32$2 = 0, i64toi32_i32$3 = 0, i64toi32_i32$1 = 0;
213+
HEAPF64[0 >> 3] = $0;
214+
i64toi32_i32$0 = HEAP32[(0 + 4 | 0) >> 2] | 0;
215+
i64toi32_i32$2 = HEAP32[0 >> 2] | 0;
216+
i64toi32_i32$1 = 2147483647;
217+
i64toi32_i32$3 = 4294967295;
218+
i64toi32_i32$1 = i64toi32_i32$0 & i64toi32_i32$1 | 0;
219+
i64toi32_i32$3 = i64toi32_i32$2 & i64toi32_i32$3 | 0;
220+
HEAPF64[0 >> 3] = $1;
221+
i64toi32_i32$2 = HEAP32[(0 + 4 | 0) >> 2] | 0;
222+
i64toi32_i32$3 = HEAP32[0 >> 2] | 0;
223+
i64toi32_i32$0 = 2147483648;
224+
i64toi32_i32$0 = i64toi32_i32$2 & i64toi32_i32$0 | 0;
225+
i64toi32_i32$2 = i64toi32_i32$3 & 0 | 0;
226+
i64toi32_i32$0 = i64toi32_i32$1 | i64toi32_i32$0 | 0;
227+
HEAP32[0 >> 2] = i64toi32_i32$3 | i64toi32_i32$2 | 0;
228+
HEAP32[(0 + 4 | 0) >> 2] = i64toi32_i32$0;
229+
return +(+HEAPF64[0 >> 3]);
230+
}
231+
232+
function copysign32($0, $1) {
233+
$0 = Math_fround($0);
234+
$1 = Math_fround($1);
235+
return Math_fround((HEAP32[0] = (HEAPF32[0] = $0, HEAP32[0]) & 2147483647 | 0 | ((HEAPF32[0] = $1, HEAP32[0]) & 2147483648 | 0) | 0, HEAPF32[0]));
236+
}
237+
209238
function __wasm_ctz_i32(x) {
210239
x = x | 0;
211240
var $1 = 0;

test/wasm2asm/float-ops.wast

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,10 @@
101101

102102
(func (export "f64.sqrt") (param $0 f64) (result f64)
103103
(f64.sqrt (get_local $0)))
104+
105+
;; copysign
106+
(func $copysign64 (param $0 f64) (param $1 f64) (result f64)
107+
(f64.copysign (get_local $0) (get_local $1)))
108+
(func $copysign32 (param $0 f32) (param $1 f32) (result f32)
109+
(f32.copysign (get_local $0) (get_local $1)))
104110
)

0 commit comments

Comments
 (0)