Skip to content

Commit

Permalink
emit bytecode for Asm.js/Wasm intrinsics
Browse files Browse the repository at this point in the history
Summary:
CallIntrinsicInst now emits bytecodes for corresponding intrinsics.
One new bytecode per intrinsic. Currently the VM treats them as
nops, except add32. New bytecodes are added at the end to save
us from touching superpack.

Reviewed By: tmikov

Differential Revision: D29733491

fbshipit-source-id: a83ca7518de5f3dd87d494ec653729b31cee6716
  • Loading branch information
Lin Cheng authored and facebook-github-bot committed Jul 24, 2021
1 parent 70b1ca8 commit 1cc6e72
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 7 deletions.
33 changes: 33 additions & 0 deletions include/hermes/BCGen/HBC/BytecodeList.def
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,39 @@ DEFINE_JUMP_3(JNotEqual)
DEFINE_JUMP_3(JStrictEqual)
DEFINE_JUMP_3(JStrictNotEqual)

#ifdef HERMES_RUN_WASM
/// Arg1 = Arg2 + Arg3 (32-bit integer addition)
DEFINE_OPCODE_3(Add32, Reg8, Reg8, Reg8)
/// Arg1 = Arg2 - Arg3 (32-bit integer subtraction)
DEFINE_OPCODE_3(Sub32, Reg8, Reg8, Reg8)
/// Arg1 = Arg2 * Arg3 (32-bit integer multiplication)
DEFINE_OPCODE_3(Mul32, Reg8, Reg8, Reg8)
/// Arg1 = Arg2 / Arg3 (32-bit signed integer division)
DEFINE_OPCODE_3(Divi32, Reg8, Reg8, Reg8)
/// Arg1 = Arg2 / Arg3 (32-bit unsigned integer division)
DEFINE_OPCODE_3(Divu32, Reg8, Reg8, Reg8)

/// Arg1 = HEAP8[Arg3] (load signed 8-bit integer)
DEFINE_OPCODE_3(Loadi8, Reg8, Reg8, Reg8)
/// Arg1 = HEAPU8[Arg3] (load unsigned 8-bit integer)
DEFINE_OPCODE_3(Loadu8, Reg8, Reg8, Reg8)
/// Arg1 = HEAP16[Arg3 >> 1] (load signed 16-bit integer)
DEFINE_OPCODE_3(Loadi16, Reg8, Reg8, Reg8)
/// Arg1 = HEAPU16[Arg3 >> 1] (load unsigned 16-bit integer)
DEFINE_OPCODE_3(Loadu16, Reg8, Reg8, Reg8)
/// Arg1 = HEAP32[Arg3 >> 2] (load signed 32-bit integer)
DEFINE_OPCODE_3(Loadi32, Reg8, Reg8, Reg8)
/// Arg1 = HEAPU32[Arg3 >> 2] (load unsigned 32-bit integer)
DEFINE_OPCODE_3(Loadu32, Reg8, Reg8, Reg8)

/// HEAP8[Arg2] = Arg3 (store signed or unsigned 8-bit integer)
DEFINE_OPCODE_3(Store8, Reg8, Reg8, Reg8)
/// HEAP16[Arg2] = Arg3 (store signed or unsigned 16-bit integer)
DEFINE_OPCODE_3(Store16, Reg8, Reg8, Reg8)
/// HEAP32[Arg2] = Arg3 (store signed or unsigned 32-bit integer)
DEFINE_OPCODE_3(Store32, Reg8, Reg8, Reg8)
#endif

// Implementations can rely on the following pairs of instructions having the
// same number and type of operands.
ASSERT_EQUAL_LAYOUT3(Call, Construct)
Expand Down
4 changes: 2 additions & 2 deletions include/hermes/BCGen/HBC/BytecodeVersion.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ namespace hermes {
namespace hbc {

// Bytecode version generated by this version of the compiler.
// Updated: Jul 06, 2021
const static uint32_t BYTECODE_VERSION = 84;
// Updated: Jul 16, 2021
const static uint32_t BYTECODE_VERSION = 85;

} // namespace hbc
} // namespace hermes
Expand Down
3 changes: 2 additions & 1 deletion include/hermes/Optimizer/Wasm/WasmIntrinsics.def
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ WASM_INTRINSICS(loadi8, 2)
WASM_INTRINSICS(loadu8, 2)
WASM_INTRINSICS(loadi16, 2)
WASM_INTRINSICS(loadu16, 2)
WASM_INTRINSICS(load32, 2)
WASM_INTRINSICS(loadi32, 2)
WASM_INTRINSICS(loadu32, 2)
WASM_INTRINSICS(loadf32, 2)
WASM_INTRINSICS(loadf64, 2)

Expand Down
64 changes: 62 additions & 2 deletions lib/BCGen/HBC/ISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,8 +1211,68 @@ void HBCISel::generateGetBuiltinClosureInst(
}

#ifdef HERMES_RUN_WASM
void HBCISel::generateCallIntrinsicInst(CallIntrinsicInst *, BasicBlock *) {
// TODO: emit CallIntrinsic Bytecode. For now this is a nop.
void HBCISel::generateCallIntrinsicInst(
CallIntrinsicInst *Inst,
BasicBlock *next) {
verifyCall(Inst);
// Currently all intrinsics takes 3 registers. However, store
// intrinsics need special handling.
auto res = encodeValue(Inst);
auto arg1 = encodeValue(Inst->getArgument(1));
auto arg2 = encodeValue(Inst->getArgument(2));

switch (Inst->getIntrinsicsIndex()) {
// Arithmetic
case WasmIntrinsics::__uasm_add32:
BCFGen_->emitAdd32(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_sub32:
BCFGen_->emitSub32(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_mul32:
BCFGen_->emitMul32(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_divi32:
BCFGen_->emitDivi32(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_divu32:
BCFGen_->emitDivu32(res, arg1, arg2);
break;

// Load
case WasmIntrinsics::__uasm_loadi8:
BCFGen_->emitLoadi8(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_loadu8:
BCFGen_->emitLoadu8(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_loadi16:
BCFGen_->emitLoadi16(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_loadu16:
BCFGen_->emitLoadu16(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_loadi32:
BCFGen_->emitLoadi32(res, arg1, arg2);
break;
case WasmIntrinsics::__uasm_loadu32:
BCFGen_->emitLoadu32(res, arg1, arg2);
break;

// Store
case WasmIntrinsics::__uasm_store8:
BCFGen_->emitStore8(arg1, arg2, encodeValue(Inst->getArgument(3)));
break;
case WasmIntrinsics::__uasm_store16:
BCFGen_->emitStore16(arg1, arg2, encodeValue(Inst->getArgument(3)));
break;
case WasmIntrinsics::__uasm_store32:
BCFGen_->emitStore32(arg1, arg2, encodeValue(Inst->getArgument(3)));
break;

default:
break;
}
}
#endif

Expand Down
77 changes: 77 additions & 0 deletions lib/VM/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3468,6 +3468,83 @@ CallResult<HermesValue> Interpreter::interpretFunction(
DISPATCH;
}

#ifdef HERMES_RUN_WASM
// Asm.js/Wasm Intrinsics
CASE(Add32) {
O1REG(Add32) = HermesValue::encodeDoubleValue(
(int32_t)(int64_t)(O2REG(Add32).getNumber() + O3REG(Add32).getNumber()));
ip = NEXTINST(Add32);
DISPATCH;
}
CASE(Sub32) {
// A nop for now.
ip = NEXTINST(Sub32);
DISPATCH;
}
CASE(Mul32) {
// A nop for now.
ip = NEXTINST(Mul32);
DISPATCH;
}
CASE(Divi32) {
// A nop for now.
ip = NEXTINST(Divi32);
DISPATCH;
}
CASE(Divu32) {
// A nop for now.
ip = NEXTINST(Divu32);
DISPATCH;
}

CASE(Loadi8) {
// A nop for now.
ip = NEXTINST(Loadi8);
DISPATCH;
}
CASE(Loadu8) {
// A nop for now.
ip = NEXTINST(Loadu8);
DISPATCH;
}
CASE(Loadi16) {
// A nop for now.
ip = NEXTINST(Loadi16);
DISPATCH;
}
CASE(Loadu16) {
// A nop for now.
ip = NEXTINST(Loadu16);
DISPATCH;
}
CASE(Loadi32) {
// A nop for now.
ip = NEXTINST(Loadi32);
DISPATCH;
}
CASE(Loadu32) {
// A nop for now.
ip = NEXTINST(Loadu32);
DISPATCH;
}

CASE(Store8) {
// A nop for now.
ip = NEXTINST(Store8);
DISPATCH;
}
CASE(Store16) {
// A nop for now.
ip = NEXTINST(Store16);
DISPATCH;
}
CASE(Store32) {
// A nop for now.
ip = NEXTINST(Store32);
DISPATCH;
}
#endif

CASE(_last) {
hermes_fatal("Invalid opcode _last");
}
Expand Down
136 changes: 136 additions & 0 deletions test/Optimizer/wasm/unsafe-intrinsics-mem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

// RUN: %hermesc -funsafe-intrinsics -target=HBC -dump-postra %s | %FileCheck --match-full-lines --check-prefix=CHKRA %s
// RUN: %hermesc -funsafe-intrinsics -target=HBC -dump-bytecode %s | %FileCheck --match-full-lines --check-prefix=CHKBC %s

// Setup Asm.js/Wasm memory
var buffer = new ArrayBuffer(131072);
var HEAP8 = new Int8Array(buffer);
var HEAP16 = new Int16Array(buffer);
var HEAP32 = new Int32Array(buffer);
var HEAPU8 = new Uint8Array(buffer);
var HEAPU16 = new Uint16Array(buffer);
var HEAPU32 = new Uint32Array(buffer);
var HEAPF32 = new Float32Array(buffer);
var HEAPF64 = new Float64Array(buffer);

for (let i = 0; i < 256; i++) {
HEAPU8[i] = i;
}

function loads(func) {
t0 = __uasm.loadi8(HEAP8, 0);
t1 = __uasm.loadu8(HEAPU8, 0);
t2 = __uasm.loadi16(HEAP16, 0);
t3 = __uasm.loadu16(HEAPU16, 0);
t4 = __uasm.loadi32(HEAP32, 0);
t5 = __uasm.loadu32(HEAPU32, 0);
}

//CHKRA-LABEL:function loads(func) : undefined
//CHKRA-NEXT:frame = []
//CHKRA-NEXT:%BB0:
//CHKRA-NEXT: %0 = HBCGetGlobalObjectInst
//CHKRA-NEXT: %1 = LoadPropertyInst %0 : object, "HEAP8" : string
//CHKRA-NEXT: %2 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %3 = HBCLoadConstInst 0 : number
//CHKRA-NEXT: %4 = CallIntrinsicInst [__uasm.loadi8_2] : number, undefined : undefined, %1, %3 : number
//CHKRA-NEXT: %5 = StorePropertyInst %4, %0 : object, "t0" : string
//CHKRA-NEXT: %6 = LoadPropertyInst %0 : object, "HEAPU8" : string
//CHKRA-NEXT: %7 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %8 = CallIntrinsicInst [__uasm.loadu8_2] : number, undefined : undefined, %6, %3 : number
//CHKRA-NEXT: %9 = StorePropertyInst %8, %0 : object, "t1" : string
//CHKRA-NEXT: %10 = LoadPropertyInst %0 : object, "HEAP16" : string
//CHKRA-NEXT: %11 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %12 = CallIntrinsicInst [__uasm.loadi16_2] : number, undefined : undefined, %10, %3 : number
//CHKRA-NEXT: %13 = StorePropertyInst %12, %0 : object, "t2" : string
//CHKRA-NEXT: %14 = LoadPropertyInst %0 : object, "HEAPU16" : string
//CHKRA-NEXT: %15 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %16 = CallIntrinsicInst [__uasm.loadu16_2] : number, undefined : undefined, %14, %3 : number
//CHKRA-NEXT: %17 = StorePropertyInst %16, %0 : object, "t3" : string
//CHKRA-NEXT: %18 = LoadPropertyInst %0 : object, "HEAP32" : string
//CHKRA-NEXT: %19 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %20 = CallIntrinsicInst [__uasm.loadi32_2] : number, undefined : undefined, %18, %3 : number
//CHKRA-NEXT: %21 = StorePropertyInst %20, %0 : object, "t4" : string
//CHKRA-NEXT: %22 = LoadPropertyInst %0 : object, "HEAPU32" : string
//CHKRA-NEXT: %23 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %24 = CallIntrinsicInst [__uasm.loadu32_2] : number, undefined : undefined, %22, %3 : number
//CHKRA-NEXT: %25 = StorePropertyInst %24, %0 : object, "t5" : string
//CHKRA-NEXT: %26 = HBCLoadConstInst undefined : undefined
//CHKRA-NEXT: %27 = ReturnInst %26 : undefined
//CHKRA-NEXT:function_end

//CHKBC-LABEL:Function<loads>(2 params, 12 registers, 0 symbols):
//CHKBC-NEXT:Offset in debug table: source 0x00b0, lexical 0x0000
//CHKBC-NEXT: GetGlobalObject r1
//CHKBC-NEXT: GetByIdShort r4, r1, 1, "HEAP8"
//CHKBC-NEXT: LoadConstZero r3
//CHKBC-NEXT: Loadi8 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 1, "t0"
//CHKBC-NEXT: GetByIdShort r4, r1, 2, "HEAPU8"
//CHKBC-NEXT: Loadu8 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 2, "t1"
//CHKBC-NEXT: GetByIdShort r4, r1, 3, "HEAP16"
//CHKBC-NEXT: Loadi16 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 3, "t2"
//CHKBC-NEXT: GetByIdShort r4, r1, 4, "HEAPU16"
//CHKBC-NEXT: Loadu16 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 4, "t3"
//CHKBC-NEXT: GetByIdShort r4, r1, 5, "HEAP32"
//CHKBC-NEXT: Loadi32 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 5, "t4"
//CHKBC-NEXT: GetByIdShort r4, r1, 6, "HEAPU32"
//CHKBC-NEXT: Loadu32 r0, r4, r3
//CHKBC-NEXT: PutById r1, r0, 6, "t5"
//CHKBC-NEXT: LoadConstUndefined r0
//CHKBC-NEXT: Ret r0

function stores(func, x) {
__uasm.store8(HEAP8, 0, x);
__uasm.store16(HEAP16, 0, x);
__uasm.store32(HEAP32, 0, x);
}

//CHKRA-LABEL:function stores(func, x) : undefined
//CHKRA-NEXT:frame = []
//CHKRA-NEXT:%BB0:
//CHKRA-NEXT: %0 = HBCLoadParamInst 2 : number
//CHKRA-NEXT: %1 = HBCGetGlobalObjectInst
//CHKRA-NEXT: %2 = LoadPropertyInst %1 : object, "HEAP8" : string
//CHKRA-NEXT: %3 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %4 = HBCLoadConstInst 0 : number
//CHKRA-NEXT: %5 = MovInst %0
//CHKRA-NEXT: %6 = CallIntrinsicInst [__uasm.store8_3] : number, undefined : undefined, %2, %4 : number, %5
//CHKRA-NEXT: %7 = LoadPropertyInst %1 : object, "HEAP16" : string
//CHKRA-NEXT: %8 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %9 = MovInst %0
//CHKRA-NEXT: %10 = CallIntrinsicInst [__uasm.store16_3] : number, undefined : undefined, %7, %4 : number, %9
//CHKRA-NEXT: %11 = LoadPropertyInst %1 : object, "HEAP32" : string
//CHKRA-NEXT: %12 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %13 = MovInst %0
//CHKRA-NEXT: %14 = CallIntrinsicInst [__uasm.store32_3] : number, undefined : undefined, %11, %4 : number, %13
//CHKRA-NEXT: %15 = HBCLoadConstInst undefined : undefined
//CHKRA-NEXT: %16 = ReturnInst %15 : undefined
//CHKRA-NEXT:function_end

//CHKBC-LABEL:Function<stores>(3 params, 14 registers, 0 symbols):
//CHKBC-NEXT:Offset in debug table: source 0x00ea, lexical 0x0000
//CHKBC-NEXT: LoadParam r2, 2
//CHKBC-NEXT: GetGlobalObject r0
//CHKBC-NEXT: GetByIdShort r6, r0, 1, "HEAP8"
//CHKBC-NEXT: LoadConstZero r5
//CHKBC-NEXT: Mov r4, r2
//CHKBC-NEXT: Store8 r6, r5, r4
//CHKBC-NEXT: GetByIdShort r6, r0, 2, "HEAP16"
//CHKBC-NEXT: Mov r4, r2
//CHKBC-NEXT: Store16 r6, r5, r4
//CHKBC-NEXT: GetByIdShort r6, r0, 3, "HEAP32"
//CHKBC-NEXT: Mov r4, r2
//CHKBC-NEXT: Store32 r6, r5, r4
//CHKBC-NEXT: LoadConstUndefined r0
//CHKBC-NEXT: Ret r0
20 changes: 18 additions & 2 deletions test/Optimizer/wasm/unsafe-intrinsics.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
*/

// RUN: %hermesc -funsafe-intrinsics -target=HBC -dump-postra %s | %FileCheck --match-full-lines --check-prefix=CHKRA %s
// RUN: %hermesc -funsafe-intrinsics -target=HBC -dump-bytecode %s | %FileCheck --match-full-lines --check-prefix=CHKBC %s

// Instrinsics that are defined should not cause any error. The call sequence is
// lowered to a CallIntrinsicInst.
function unsafeIntrinsics(func) {
t0 = __uasm.add32(1, 2);
t1 = __uasm.modi32(42, 7);
t1 = __uasm.mul32(42, 7);
return t0 + t1;
}
//CHKRA-LABEL:function unsafeIntrinsics(func) : string|number
Expand All @@ -26,14 +27,29 @@ function unsafeIntrinsics(func) {
//CHKRA-NEXT: %6 = HBCLoadConstInst 42 : number
//CHKRA-NEXT: %7 = HBCLoadConstInst 7 : number
//CHKRA-NEXT: %8 = ImplicitMovInst undefined : undefined
//CHKRA-NEXT: %9 = CallIntrinsicInst [__uasm.modi32_2] : number, undefined : undefined, %6 : number, %7 : number
//CHKRA-NEXT: %9 = CallIntrinsicInst [__uasm.mul32_2] : number, undefined : undefined, %6 : number, %7 : number
//CHKRA-NEXT: %10 = StorePropertyInst %9, %4 : object, "t1" : string
//CHKRA-NEXT: %11 = TryLoadGlobalPropertyInst %4 : object, "t0" : string
//CHKRA-NEXT: %12 = TryLoadGlobalPropertyInst %4 : object, "t1" : string
//CHKRA-NEXT: %13 = BinaryOperatorInst '+', %11, %12
//CHKRA-NEXT: %14 = ReturnInst %13 : string|number
//CHKRA-NEXT:function_end

//CHKBC-LABEL:Function<unsafeIntrinsics>(2 params, 12 registers, 0 symbols):
//CHKBC-NEXT:Offset in debug table: source 0x000a, lexical 0x0000
//CHKBC-NEXT: LoadConstUInt8 r4, 1
//CHKBC-NEXT: LoadConstUInt8 r3, 2
//CHKBC-NEXT: Add32 r1, r4, r3
//CHKBC-NEXT: GetGlobalObject r0
//CHKBC-NEXT: PutById r0, r1, 1, "t0"
//CHKBC-NEXT: LoadConstUInt8 r4, 42
//CHKBC-NEXT: LoadConstUInt8 r3, 7
//CHKBC-NEXT: Mul32 r1, r4, r3
//CHKBC-NEXT: PutById r0, r1, 2, "t1"
//CHKBC-NEXT: TryGetById r1, r0, 1, "t0"
//CHKBC-NEXT: TryGetById r0, r0, 2, "t1"
//CHKBC-NEXT: Add r0, r1, r0
//CHKBC-NEXT: Ret r0

// If namespace is not "__uasm", CallIntrinsicInst should not be emitted.
function incorrectNamespace(func) {
Expand Down
Loading

0 comments on commit 1cc6e72

Please sign in to comment.