Skip to content

Commit bc7f3c3

Browse files
committed
[GR-65743] GraalWasm: Volatile LinkState field hinders optimizations.
PullRequest: graal/21073
2 parents c7ff9b3 + 9da987e commit bc7f3c3

File tree

3 files changed

+56
-21
lines changed

3 files changed

+56
-21
lines changed

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/Linker.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,13 @@ public void tryLink(WasmInstance instance) {
130130
// compilation, and this check will fold away.
131131
// If the code is compiled synchronously, then this check will persist in the compiled code.
132132
// We nevertheless invalidate the compiled code that reaches this point.
133-
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, !instance.isLinkCompleted())) {
133+
if (!instance.isLinkCompleted()) {
134+
tryLinkOutsidePartialEvaluation(instance);
135+
}
136+
}
137+
138+
public void tryLinkFastPath(WasmInstance instance) {
139+
if (CompilerDirectives.injectBranchProbability(CompilerDirectives.SLOWPATH_PROBABILITY, !instance.isLinkCompletedFastPath())) {
134140
tryLinkOutsidePartialEvaluation(instance);
135141
}
136142
}

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/RuntimeState.java

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,14 @@
4040
*/
4141
package org.graalvm.wasm;
4242

43+
import java.lang.invoke.MethodHandles;
44+
import java.lang.invoke.VarHandle;
45+
4346
import org.graalvm.wasm.constants.BytecodeBitEncoding;
4447
import org.graalvm.wasm.memory.WasmMemory;
4548

4649
import com.oracle.truffle.api.CallTarget;
50+
import com.oracle.truffle.api.CompilerAsserts;
4751
import com.oracle.truffle.api.CompilerDirectives;
4852
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
4953
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -108,10 +112,20 @@ public abstract class RuntimeState {
108112
*/
109113
private final int droppedDataInstanceOffset;
110114

111-
@CompilationFinal private volatile Linker.LinkState linkState;
115+
@CompilationFinal private Linker.LinkState linkState;
112116

113117
@CompilationFinal private int startFunctionIndex;
114118

119+
static final VarHandle LINK_STATE;
120+
121+
static {
122+
try {
123+
LINK_STATE = MethodHandles.lookup().findVarHandle(RuntimeState.class, "linkState", Linker.LinkState.class);
124+
} catch (NoSuchFieldException | IllegalAccessException e) {
125+
throw new ExceptionInInitializerError(e);
126+
}
127+
}
128+
115129
private void ensureGlobalsCapacity(int index) {
116130
while (index >= globalAddresses.length) {
117131
final int[] nGlobalAddresses = new int[globalAddresses.length * 2];
@@ -153,30 +167,22 @@ public RuntimeState(WasmStore store, WasmModule module, int numberOfFunctions, i
153167

154168
private void checkNotLinked() {
155169
// The symbol table must be read-only after the module gets linked.
156-
if (linkState == Linker.LinkState.linked) {
170+
Linker.LinkState state = linkState();
171+
if (state == Linker.LinkState.linked || state == Linker.LinkState.failed) {
157172
throw CompilerDirectives.shouldNotReachHere("The engine tried to modify the instance after linking.");
158173
}
159174
}
160175

161176
public void setLinkInProgress() {
162-
if (linkState != Linker.LinkState.nonLinked) {
163-
throw CompilerDirectives.shouldNotReachHere("Can only switch to in-progress state when not linked.");
164-
}
165-
this.linkState = Linker.LinkState.inProgress;
177+
setLinkState(Linker.LinkState.nonLinked, Linker.LinkState.inProgress, "Can only switch to in-progress state when not linked.");
166178
}
167179

168180
public void setLinkCompleted() {
169-
if (linkState != Linker.LinkState.inProgress) {
170-
throw CompilerDirectives.shouldNotReachHere("Can only switch to linked state when linking is in-progress.");
171-
}
172-
this.linkState = Linker.LinkState.linked;
181+
setLinkState(Linker.LinkState.inProgress, Linker.LinkState.linked, "Can only switch to linked state when linking is in-progress.");
173182
}
174183

175184
public void setLinkFailed() {
176-
if (linkState != Linker.LinkState.inProgress) {
177-
throw CompilerDirectives.shouldNotReachHere("Can only switch to failed state when linking is in-progress.");
178-
}
179-
this.linkState = Linker.LinkState.failed;
185+
setLinkState(Linker.LinkState.inProgress, Linker.LinkState.failed, "Can only switch to failed state when linking is in-progress.");
180186
}
181187

182188
public WasmStore store() {
@@ -188,23 +194,46 @@ public WasmContext context() {
188194
}
189195

190196
public Linker.LinkState linkState() {
191-
return linkState;
197+
CompilerAsserts.neverPartOfCompilation();
198+
return (Linker.LinkState) LINK_STATE.getVolatile(this);
199+
}
200+
201+
private void setLinkState(Linker.LinkState expectedState, Linker.LinkState newState, String message) {
202+
assert expectedState != Linker.LinkState.linked && expectedState != Linker.LinkState.failed : expectedState;
203+
assert Thread.holdsLock(store());
204+
if (!LINK_STATE.compareAndSet(this, expectedState, newState)) {
205+
/*
206+
* setLinkState is always invoked while the linker is holding a store lock, so we should
207+
* always see the expected state here and the CAS should never fail.
208+
*/
209+
throw CompilerDirectives.shouldNotReachHere(message);
210+
}
192211
}
193212

194213
public boolean isNonLinked() {
195-
return linkState == Linker.LinkState.nonLinked;
214+
return linkState() == Linker.LinkState.nonLinked;
196215
}
197216

198217
public boolean isLinkInProgress() {
199-
return linkState == Linker.LinkState.inProgress;
218+
return linkState() == Linker.LinkState.inProgress;
200219
}
201220

202221
public boolean isLinkCompleted() {
222+
return linkState() == Linker.LinkState.linked;
223+
}
224+
225+
/**
226+
* Non-volatile link state check for use in compiled code. May read a stale value, which is OK,
227+
* since in that case we'll just (deoptimize and) enter the slow path, and check again. Once
228+
* this method has returned true, i.e., we've reached the state {@link Linker.LinkState#linked},
229+
* we can safely rely on the module to be linked, and stay linked, since it is a final state.
230+
*/
231+
public boolean isLinkCompletedFastPath() {
203232
return linkState == Linker.LinkState.linked;
204233
}
205234

206235
public boolean isLinkFailed() {
207-
return linkState == Linker.LinkState.failed;
236+
return linkState() == Linker.LinkState.failed;
208237
}
209238

210239
public SymbolTable symbolTable() {

wasm/src/org.graalvm.wasm/src/org/graalvm/wasm/nodes/WasmRootNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ public final void tryInitialize(WasmInstance instance) {
8484
// We want to ensure that linking always precedes the running of the WebAssembly code.
8585
// This linking should be as late as possible, because a WebAssembly context should
8686
// be able to parse multiple modules before the code gets run.
87-
if (!instance.isLinkCompleted()) {
87+
if (!instance.isLinkCompletedFastPath()) {
8888
nonLinkedProfile.enter();
89-
instance.store().linker().tryLink(instance);
89+
instance.store().linker().tryLinkFastPath(instance);
9090
}
9191
}
9292

0 commit comments

Comments
 (0)