Skip to content
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

Improve inlining of <| on (GraalVM EE) #6384

Merged
merged 6 commits into from
Apr 25, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
45 changes: 0 additions & 45 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Any.enso
Original file line number Diff line number Diff line change
Expand Up @@ -371,25 +371,6 @@ type Any
Nothing -> Nothing
a -> f a

## Applies the function `self` to the provided argument.

Arguments:
- argument: The argument to apply `self` to.

? Piping Blocks to Functions
This construction is particularly useful for passing a block as an argument
to a function. This means that you can compute more sophisticated values
in-line, as shown in the example below.

> Example
Applying a function to a block.

(x -> x + 1) <|
y = 1 ^ 3
3 + y
<| : Any -> Any
<| self ~argument = self argument

## Applies the function on the right hand side to the argument on the left.

Arguments
Expand All @@ -411,32 +392,6 @@ type Any
|> : (Any -> Any) -> Any
|> self ~function = function self

## Composes two functions together, for `f << g` creating the function
composition `f ∘ g` (equivalent to `x -> f (g x)`).

Arguments:
- that: The function to compose with `self`.

> Example
Multiply by 2 and then add 1 as a function applied to 2.

(+1 << *2) 2
<< : (Any -> Any) -> (Any -> Any)
<< self ~that = x -> self (that x)

## Composes two functions together in the forward direction, for `f >> g`
creating the function composition `g ∘ f` (equivalent to `x -> g (f (x))`).

Arguments:
- that: The function to compose with `self`.

> Example
Add one and then multiply by two as a function applied to 2.

(+1 >> *2) 2
>> : (Any -> Any) -> (Any -> Any)
>> self ~that = x -> that (self x)

## Checks if any warnings (either all or of a specified type) are attached to the value.

Arguments:
Expand Down
46 changes: 46 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Function.enso
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,52 @@ import project.Data.Vector.Vector
@Builtin_Type
type Function

## Applies the function `self` to the provided argument.

Arguments:
- argument: The argument to apply `self` to.

? Piping Blocks to Functions
This construction is particularly useful for passing a block as an argument
to a function. This means that you can compute more sophisticated values
in-line, as shown in the example below.

> Example
Applying a function to a block.

(x -> x + 1) <|
y = 1 ^ 3
3 + y
<| : Any -> Any
<| self ~argument = @Builtin_Method "Function.<|"

## Composes two functions together, for `f << g` creating the function
composition `f ∘ g` (equivalent to `x -> f (g x)`).

Arguments:
- that: The function to compose with `self`.

> Example
Multiply by 2 and then add 1 as a function applied to 2.

(+1 << *2) 2
<< : (Any -> Any) -> (Any -> Any)
JaroslavTulach marked this conversation as resolved.
Show resolved Hide resolved
<< self ~that = x -> self (that x)

## Composes two functions together in the forward direction, for `f >> g`
creating the function composition `g ∘ f` (equivalent to `x -> g (f (x))`).

Arguments:
- that: The function to compose with `self`.

> Example
Add one and then multiply by two as a function applied to 2.

(+1 >> *2) 2
>> : (Any -> Any) -> (Any -> Any)
>> self ~that = x -> that (self x)


## An identity function which returns the provided argument.

Arguments:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class IfVsCaseBenchmarks extends TestBase {
private Value ifBench3;
private Value caseBench3;
private Value ifBench6;
private Value ifBench6In;
private Value caseBench6;
private Value createVec;
private Value inputVec;
Expand Down Expand Up @@ -98,6 +99,17 @@ public void initializeBench(BenchmarkParams params) throws IOException {
if curr.f6.not then acc else
acc + 1

if_bench_6_in : Vector My_Type -> Integer
if_bench_6_in vec =
vec.fold 0 acc-> curr->
curr.f1.not.if_then_else acc <|
curr.f2.not.if_then_else acc <|
curr.f3.not.if_then_else acc <|
curr.f4.not.if_then_else acc <|
curr.f5.not.if_then_else acc <|
curr.f6.not.if_then_else acc <|
acc + 1

case_bench_6 : Vector My_Type -> Integer
case_bench_6 vec =
vec.fold 0 acc-> curr->
Expand Down Expand Up @@ -130,6 +142,7 @@ public void initializeBench(BenchmarkParams params) throws IOException {
ifBench3 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "if_bench_3"));
caseBench3 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "case_bench_3"));
ifBench6 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "if_bench_6"));
ifBench6In = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "if_bench_6_in"));
caseBench6 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "case_bench_6"));
createVec = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "create_vec"));
// So far, input is a vector of My_Type.Value with all fields set to True
Expand All @@ -156,6 +169,12 @@ public void ifBench6() {
checkResult(res);
}

@Benchmark
public void ifBench6In() {
Value res = ifBench6In.execute(inputVec);
checkResult(res);
}

@Benchmark
public void caseBench3() {
Value res = caseBench3.execute(inputVec);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -620,9 +620,9 @@ Object doFallback(
@CachedLibrary(limit = "10") TypesLibrary methods,
@CachedLibrary(limit = "10") InteropLibrary interop,
@CachedLibrary(limit = "10") WarningsLibrary warnings,
@Cached MethodResolverNode anyResolverNode) {
@Cached MethodResolverNode resolverNode) {
var ctx = EnsoContext.get(this);
Function function = anyResolverNode.expectNonNull(self, ctx.getBuiltins().any(), symbol);
Function function = resolverNode.expectNonNull(self, ctx.getBuiltins().function(), symbol);
return invokeFunctionNode.execute(function, frame, state, arguments);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ protected BuiltinRootNode(EnsoLanguage language) {
* @return new node to use to call this builtin
*/
public DirectCallNode createDirectCallNode() {
return DirectCallNode.create(getCallTarget());
return DirectCallNode.create(cloneUninitialized().getCallTarget());
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cloning each BuiltinRootNode before use - builtins are lightweight and cloning helps to overcome Truffle recursive inlining limit.

Builtins that don't require VirtualFrame in their method signature are explicitly inlined by #6255 (comment) - those that require VirtualFrame are "cloned" before each use. That makes Truffle believe that each call into the builtin is a call the different function.

Such a cloning is not as effective direct inlining, but certainly more effective than giving up on inlining completely due to recursive limit for inlining.

}

/**
Expand Down