Skip to content

Optimize expandType for JS target #2148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

import { BuiltinNames } from "./builtins";
import { Target } from "./common";
import {
isHighSurrogate,
isLowSurrogate,
combineSurrogates,
SURROGATE_HIGH,
SURROGATE_LOW
import {
isHighSurrogate,
isLowSurrogate,
combineSurrogates,
SURROGATE_HIGH,
SURROGATE_LOW
} from "./util";
import * as binaryen from "./glue/binaryen";

Expand Down Expand Up @@ -2619,8 +2619,15 @@ export function expandType(type: TypeRef): TypeRef[] {
var cArr = binaryen._malloc(<usize>arity << 2);
binaryen._BinaryenTypeExpand(type, cArr);
var types = new Array<TypeRef>(arity);
for (let i: u32 = 0; i < arity; ++i) {
types[i] = binaryen.__i32_load(cArr + (<usize>i << 2));
if (!ASC_TARGET) {
let ptr = cArr >>> 2;
for (let i: u32 = 0; i < arity; ++i) {
types[i] = binaryen.HEAPU32[ptr + i];
}
} else {
for (let i: u32 = 0; i < arity; ++i) {
types[i] = binaryen.__i32_load(cArr + (<usize>i << 2));
}
}
binaryen._free(cArr);
return types;
Expand Down
44 changes: 25 additions & 19 deletions src/passes/shadowstack.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,52 @@
/**
* @fileoverview Shadow stack instrumentation for a precise GC.
*
*
* Instruments function arguments and local assignments marked with a 'tostack'
* call to also do stores to a shadow stack of managed values only.
*
*
* Consider a simple call to a function looking like the following, taking
* managed arguments, plus assigning managed values to locals:
*
*
* function foo(a: Obj, b: Obj): Obj {
* var c = __tostack(a) // slot 2
* __collect()
* return b
* }
*
*
* foo(__tostack(a), __tostack(b)) // slot 0, 1
*
*
* At the call to `__collect()` the 32-bit stack frame of the function is:
*
*
* Offset | Value stored
* -------|----------------------------
* 0 | First managed argument 'a'
* 4 | Second managed argument 'b'
* -------|----------------------------
* 8 | First managed local 'c'
*
*
* We are splitting the frame in two halves as annotated since both halves are
* only known separately for indirect calls, with the first half becoming an
* extension of the calling function's stack frame by means of treating the
* arguments as if these were locals beyond the caller's `numLocals`. Function
* arguments stay a bit longer on the stack than usually, but we also don't have
* to modify the stack pointer pre-call at all this way. The caller's amended
* stack frame when assuming one managed local may look like this:
*
*
* Offset | Value stored
* -------|----------------------------
* 0 | First managed local '?'
* 4 | Extended with first managed argument 'a'
* 8 | Extended with second managed argument 'b'
*
*
* with the callee's stack frame becoming just:
*
*
* Offset | Value stored
* -------|----------------------------
* 0 | First managed local 'c'
*
*
* Instrumentation added below looks about like the following, with the stack
* growing downwards and 't' and 'r' being new temporary locals:
*
*
* // callee frameSize = 1 * sizeof<usize>()
* function foo(a: usize, b: usize): usize {
* memory.fill(__stack_pointer -= frameSize, 0, frameSize)
Expand All @@ -56,7 +56,7 @@
* __stack_pointer += frameSize
* return r
* }
*
*
* // caller frameSize = (numLocalSlots + 2 [by extension]) * sizeof<usize>()
* (
* r = foo(
Expand All @@ -69,14 +69,14 @@
* ),
* r
* )
*
*
* Also note that we have to `memory.fill` the second half because the first
* assignment to a local may happen at a later point within the function. The
* invariant we need to maintain for a precise GC is that it only sees zeroes
* or valid pointers, but never an invalid pointer left on the stack earlier.
* Since most frames are small, we unroll a sequence of `store`s up to a frame
* size of 16 bytes, and `memory.fill`, if available, beyond.
*
*
* @license Apache-2.0
*/

Expand Down Expand Up @@ -155,7 +155,10 @@ type TempMap = Map<TypeRef,LocalIndex>;

/** Attempts to match the `__tostack(value)` pattern. Returns `value` if a match, otherwise `0`. */
function matchPattern(module: Module, expr: ExpressionRef): ExpressionRef {
if (_BinaryenExpressionGetId(expr) == ExpressionId.Call && module.readStringCached(_BinaryenCallGetTarget(expr)) == BuiltinNames.tostack) {
if (
_BinaryenExpressionGetId(expr) == ExpressionId.Call &&
module.readStringCached(_BinaryenCallGetTarget(expr)) == BuiltinNames.tostack
) {
assert(_BinaryenCallGetNumOperands(expr) == 1);
return _BinaryenCallGetOperandAt(expr, 0);
}
Expand Down Expand Up @@ -320,7 +323,10 @@ export class ShadowStackPass extends Pass {
module.global_get(BuiltinNames.stack_pointer, this.ptrType),
module.global_get(BuiltinNames.data_end, this.ptrType)
),
this.compiler.makeStaticAbort(this.compiler.ensureStaticString("stack overflow"), this.compiler.program.nativeSource)
this.compiler.makeStaticAbort(
this.compiler.ensureStaticString("stack overflow"),
this.compiler.program.nativeSource
)
)
);
}
Expand Down Expand Up @@ -579,7 +585,7 @@ export class ShadowStackPass extends Pass {
);
// memory.fill(__stack_pointer, 0, frameSize)
this.makeStackFill(frameSize, stmts);

// Handle implicit return
let body = _BinaryenFunctionGetBody(func);
let bodyType = _BinaryenExpressionGetType(body);
Expand Down Expand Up @@ -675,4 +681,4 @@ class InstrumentReturns extends Pass {
);
this.replaceCurrent(module.flatten(stmts, TypeRef.Unreachable));
}
}
}