Skip to content

Commit db37514

Browse files
committed
Add an alwaysInline builtin that inlines any inner function calls
This builtin operates similar to unchecked, by setting a new FlowFlag that is checked in makeCallDirect in the area where the @inline decorator is checked, thereby achieving the same functionality as it.
1 parent 40850fe commit db37514

File tree

8 files changed

+378
-5
lines changed

8 files changed

+378
-5
lines changed

src/builtins.ts

+19
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ export namespace BuiltinNames {
191191
export const assert = "~lib/builtins/assert";
192192
export const call_indirect = "~lib/builtins/call_indirect";
193193
export const unchecked = "~lib/builtins/unchecked";
194+
export const alwaysInline = "~lib/builtins/alwaysInline";
194195
export const instantiate = "~lib/builtins/instantiate";
195196
export const idof = "~lib/builtins/idof";
196197

@@ -3611,6 +3612,24 @@ function builtin_unchecked(ctx: BuiltinFunctionContext): ExpressionRef {
36113612
}
36123613
builtinFunctions.set(BuiltinNames.unchecked, builtin_unchecked);
36133614

3615+
// alwaysInline(expr: *) -> *
3616+
function builtin_alwaysInline(ctx: BuiltinFunctionContext): ExpressionRef {
3617+
let compiler = ctx.compiler;
3618+
let module = compiler.module;
3619+
if (
3620+
checkTypeAbsent(ctx) |
3621+
checkArgsRequired(ctx, 1)
3622+
) return module.unreachable();
3623+
let flow = compiler.currentFlow;
3624+
let alreadyInline = flow.is(FlowFlags.InlineContext);
3625+
if (!alreadyInline) flow.set(FlowFlags.InlineContext);
3626+
// eliminate unnecessary tees by preferring contextualType(=void)
3627+
let expr = compiler.compileExpression(ctx.operands[0], ctx.contextualType);
3628+
if (!alreadyInline) flow.unset(FlowFlags.InlineContext);
3629+
return expr;
3630+
}
3631+
builtinFunctions.set(BuiltinNames.alwaysInline, builtin_alwaysInline);
3632+
36143633
// call_indirect<T?>(index: u32, ...args: *[]) -> T
36153634
function builtin_call_indirect(ctx: BuiltinFunctionContext): ExpressionRef {
36163635
let compiler = ctx.compiler;

src/compiler.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6255,7 +6255,8 @@ export class Compiler extends DiagnosticEmitter {
62556255
}
62566256

62576257
// Inline if explicitly requested
6258-
if (instance.hasDecorator(DecoratorFlags.Inline) && (!instance.is(CommonFlags.Overridden) || reportNode.isAccessOnSuper)) {
6258+
let inlineRequested = instance.hasDecorator(DecoratorFlags.Inline) || this.currentFlow.is(FlowFlags.InlineContext);
6259+
if (inlineRequested && (!instance.is(CommonFlags.Overridden) || reportNode.isAccessOnSuper)) {
62596260
assert(!instance.is(CommonFlags.Stub)); // doesn't make sense
62606261
let inlineStack = this.inlineStack;
62616262
if (inlineStack.includes(instance)) {

src/flow.ts

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export const enum FlowFlags {
146146
UncheckedContext = 1 << 15,
147147
/** This is a flow compiling a constructor parameter. */
148148
CtorParamContext = 1 << 16,
149+
/** This is a flow where all function calls are inlined if possible. */
150+
InlineContext = 1 << 17,
149151

150152
// masks
151153

std/assembly/builtins.ts

+4
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ export declare function assert<T>(isTrueish: T, message?: string): T;
202202
@unsafe @builtin
203203
export declare function unchecked<T>(expr: T): T;
204204

205+
// @ts-ignore: decorator
206+
@unsafe @builtin
207+
export declare function alwaysInline<T>(expr: T): T;
208+
205209
// @ts-ignore: decorator
206210
@unsafe @builtin
207211
export declare function call_indirect<T>(index: u32, ...args: auto[]): T;

std/assembly/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ declare function idof<T>(): u32;
222222
declare function changetype<T>(value: any): T;
223223
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
224224
declare function unchecked<T>(value: T): T;
225+
/** Explicitly requests inlined function calls on the provided expression wherever possible. */
226+
declare function alwaysInline<T>(value: T): T;
225227
/** Emits a `call_indirect` instruction, calling the specified function in the function table by index with the specified arguments. Does result in a runtime error if the arguments do not match the called function. */
226228
declare function call_indirect<T>(index: u32, ...args: unknown[]): T;
227229
/** Instantiates a new instance of `T` using the specified constructor arguments. */

tests/compiler/inlining.debug.wat

+191-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
(type $4 (func (param i32 i32) (result i32)))
77
(type $5 (func (result i32)))
88
(type $6 (func (param i32 i32 i32)))
9-
(type $7 (func (param i32 i32 i32 i32)))
10-
(type $8 (func (param i32 i32 i64) (result i32)))
9+
(type $7 (func (param i32 i32 i32) (result f64)))
10+
(type $8 (func (param i32 i32 i32 i32)))
11+
(type $9 (func (param i32 i32 i64) (result i32)))
12+
(type $10 (func (param f64) (result f64)))
1113
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
1214
(global $inlining/constantGlobal i32 (i32.const 1))
1315
(global $~argumentsLength (mut i32) (i32.const 0))
@@ -44,6 +46,7 @@
4446
(table $0 2 2 funcref)
4547
(elem $0 (i32.const 1) $inlining/func_fe~anonymous|0)
4648
(export "test" (func $inlining/test))
49+
(export "bar" (func $inlining/bar))
4750
(export "memory" (memory $0))
4851
(start $~start)
4952
(func $inlining/test (result i32)
@@ -2573,6 +2576,192 @@
25732576
global.set $~lib/rt/itcms/fromSpace
25742577
call $inlining/test_ctor
25752578
)
2579+
(func $~lib/math/NativeMath.cbrt (param $x f64) (result f64)
2580+
(local $u i64)
2581+
(local $hx i32)
2582+
(local $t f64)
2583+
(local $r f64)
2584+
(local $s f64)
2585+
local.get $x
2586+
i64.reinterpret_f64
2587+
local.set $u
2588+
local.get $u
2589+
i64.const 32
2590+
i64.shr_u
2591+
i32.wrap_i64
2592+
i32.const 2147483647
2593+
i32.and
2594+
local.set $hx
2595+
local.get $hx
2596+
i32.const 2146435072
2597+
i32.ge_u
2598+
if
2599+
local.get $x
2600+
local.get $x
2601+
f64.add
2602+
return
2603+
end
2604+
local.get $hx
2605+
i32.const 1048576
2606+
i32.lt_u
2607+
if
2608+
local.get $x
2609+
f64.const 18014398509481984
2610+
f64.mul
2611+
i64.reinterpret_f64
2612+
local.set $u
2613+
local.get $u
2614+
i64.const 32
2615+
i64.shr_u
2616+
i32.wrap_i64
2617+
i32.const 2147483647
2618+
i32.and
2619+
local.set $hx
2620+
local.get $hx
2621+
i32.const 0
2622+
i32.eq
2623+
if
2624+
local.get $x
2625+
return
2626+
end
2627+
local.get $hx
2628+
i32.const 3
2629+
i32.div_u
2630+
i32.const 696219795
2631+
i32.add
2632+
local.set $hx
2633+
else
2634+
local.get $hx
2635+
i32.const 3
2636+
i32.div_u
2637+
i32.const 715094163
2638+
i32.add
2639+
local.set $hx
2640+
end
2641+
local.get $u
2642+
i64.const 1
2643+
i64.const 63
2644+
i64.shl
2645+
i64.and
2646+
local.set $u
2647+
local.get $u
2648+
local.get $hx
2649+
i64.extend_i32_u
2650+
i64.const 32
2651+
i64.shl
2652+
i64.or
2653+
local.set $u
2654+
local.get $u
2655+
f64.reinterpret_i64
2656+
local.set $t
2657+
local.get $t
2658+
local.get $t
2659+
f64.mul
2660+
local.get $t
2661+
local.get $x
2662+
f64.div
2663+
f64.mul
2664+
local.set $r
2665+
local.get $t
2666+
f64.const 1.87595182427177
2667+
local.get $r
2668+
f64.const -1.8849797954337717
2669+
local.get $r
2670+
f64.const 1.6214297201053545
2671+
f64.mul
2672+
f64.add
2673+
f64.mul
2674+
f64.add
2675+
local.get $r
2676+
local.get $r
2677+
f64.mul
2678+
local.get $r
2679+
f64.mul
2680+
f64.const -0.758397934778766
2681+
local.get $r
2682+
f64.const 0.14599619288661245
2683+
f64.mul
2684+
f64.add
2685+
f64.mul
2686+
f64.add
2687+
f64.mul
2688+
local.set $t
2689+
local.get $t
2690+
i64.reinterpret_f64
2691+
i64.const 2147483648
2692+
i64.add
2693+
i64.const -1073741824
2694+
i64.and
2695+
f64.reinterpret_i64
2696+
local.set $t
2697+
local.get $t
2698+
local.get $t
2699+
f64.mul
2700+
local.set $s
2701+
local.get $x
2702+
local.get $s
2703+
f64.div
2704+
local.set $r
2705+
local.get $r
2706+
local.get $t
2707+
f64.sub
2708+
f64.const 2
2709+
local.get $t
2710+
f64.mul
2711+
local.get $r
2712+
f64.add
2713+
f64.div
2714+
local.set $r
2715+
local.get $t
2716+
local.get $t
2717+
local.get $r
2718+
f64.mul
2719+
f64.add
2720+
local.set $t
2721+
local.get $t
2722+
return
2723+
)
2724+
(func $inlining/foo (param $a i32) (param $b i32) (param $c i32) (result f64)
2725+
local.get $a
2726+
f64.convert_i32_s
2727+
local.get $b
2728+
f64.convert_i32_s
2729+
call $~lib/math/NativeMath.cbrt
2730+
f64.mul
2731+
local.get $c
2732+
f64.convert_i32_s
2733+
f64.add
2734+
return
2735+
)
2736+
(func $inlining/bar (param $a i32) (param $b i32) (param $c i32) (result f64)
2737+
(local $a|3 i32)
2738+
(local $b|4 i32)
2739+
(local $c|5 i32)
2740+
block $inlining/foo|inlined.0 (result f64)
2741+
local.get $a
2742+
local.set $a|3
2743+
local.get $b
2744+
local.set $b|4
2745+
local.get $c
2746+
local.set $c|5
2747+
local.get $a|3
2748+
f64.convert_i32_s
2749+
local.get $b|4
2750+
f64.convert_i32_s
2751+
call $~lib/math/NativeMath.cbrt
2752+
f64.mul
2753+
local.get $c|5
2754+
f64.convert_i32_s
2755+
f64.add
2756+
br $inlining/foo|inlined.0
2757+
end
2758+
local.get $b
2759+
local.get $a
2760+
local.get $c
2761+
call $inlining/foo
2762+
f64.div
2763+
return
2764+
)
25762765
(func $~lib/rt/__visit_globals (param $0 i32)
25772766
(local $1 i32)
25782767
i32.const 304

0 commit comments

Comments
 (0)