Skip to content

Commit 1c9cbf5

Browse files
authored
Fuse tuple field assignments (#1299)
1 parent a1beb13 commit 1c9cbf5

2 files changed

Lines changed: 91 additions & 5 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtranslation/EliminateTuples.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -580,17 +580,14 @@ public void visit(ImMethodCall mc) {
580580
/**
581581
* Select tuple storage before expanding it. Expanding first turns one array/member read into
582582
* reads of every scalar backing variable, which then have to be preserved through discard
583-
* calls because those reads can fail. A source-level field read only needs the selected
584-
* backing component.
583+
* calls because those reads can fail. A source-level field read or write only needs the
584+
* selected backing component.
585585
*/
586586
private static @org.eclipse.jdt.annotation.Nullable ImExpr selectTupleStorageComponent(
587587
ImTupleSelection selection, ImTranslator translator, ImFunction f) {
588588
List<Integer> componentPath = new ArrayList<>();
589589
ImExpr storage = selection;
590590
while (storage instanceof ImTupleSelection current) {
591-
if (current.isUsedAsLValue()) {
592-
return null;
593-
}
594591
componentPath.add(current.getTupleIndex());
595592
storage = current.getTupleExpr();
596593
}

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaBackendAuditTests.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,95 @@ public void tupleFieldReadsOnlyLoadTheSelectedStorageComponent() throws IOExcept
282282
|| memberRead.contains("__wurst_tuple_discard_"));
283283
}
284284

285+
@Test
286+
public void tupleFieldAssignmentsOnlyTouchTheSelectedStorageComponent() throws IOException {
287+
String[] source = {
288+
"package Test",
289+
"native testSuccess()",
290+
"tuple vec3(real x, real y, real z)",
291+
"tuple segment(vec3 start, vec3 finish)",
292+
"vec3 array points",
293+
"segment array segments",
294+
"int indexCalls",
295+
"int trace",
296+
"@noinline function nextIndex() returns int",
297+
" indexCalls++",
298+
" trace = trace * 10 + 1",
299+
" return 2",
300+
"@noinline function nextValue() returns real",
301+
" trace = trace * 10 + 2",
302+
" return 21.",
303+
"@noinline function setAt(int index, real value)",
304+
" points[index].z = value",
305+
"@noinline function setAtNext()",
306+
" points[nextIndex()].y = nextValue()",
307+
"@noinline function addAt(int index, real value)",
308+
" points[index].x += value",
309+
"@noinline function setNested(int index, real value)",
310+
" segments[index].finish.y = value",
311+
"@noinline function replaceAt(int index, vec3 value)",
312+
" points[index] = value",
313+
"class Entity",
314+
" vec3 pos",
315+
"@noinline function setPos(Entity entity, real value)",
316+
" entity.pos.z = value",
317+
"init",
318+
" points[1] = vec3(1., 2., 3.)",
319+
" points[2] = vec3(4., 5., 6.)",
320+
" segments[1] = segment(vec3(7., 8., 9.), vec3(10., 11., 12.))",
321+
" let entity = new Entity()",
322+
" entity.pos = vec3(13., 14., 15.)",
323+
" setAt(1, 20.)",
324+
" setAtNext()",
325+
" addAt(1, 22.)",
326+
" setNested(1, 23.)",
327+
" setPos(entity, 24.)",
328+
" replaceAt(2, vec3(25., 26., 27.))",
329+
" if points[1] == vec3(23., 2., 20.) and points[2] == vec3(25., 26., 27.)",
330+
" and segments[1] == segment(vec3(7., 8., 9.), vec3(10., 23., 12.))",
331+
" and entity.pos == vec3(13., 14., 24.) and indexCalls == 1 and trace == 12",
332+
" testSuccess()"
333+
};
334+
test().testLua(true).executeProg().lines(source);
335+
336+
String compiled = compileOptimizedLua(
337+
"tupleFieldAssignmentsOnlyTouchTheSelectedStorageComponentOptimized", source);
338+
String plainWrite = luaFunctionBody(compiled, "setAt");
339+
String effectfulWrite = luaFunctionBody(compiled, "setAtNext");
340+
String readModifyWrite = luaFunctionBody(compiled, "addAt");
341+
String nestedWrite = luaFunctionBody(compiled, "setNested");
342+
String memberWrite = luaFunctionBody(compiled, "setPos");
343+
String wholeTupleWrite = luaFunctionBody(compiled, "replaceAt");
344+
345+
assertTrue(plainWrite.contains("points_z["));
346+
assertFalse(plainWrite.contains("points_x["));
347+
assertFalse(plainWrite.contains("points_y["));
348+
assertTrue(effectfulWrite.contains("points_y["));
349+
assertFalse(effectfulWrite.contains("points_x["));
350+
assertFalse(effectfulWrite.contains("points_z["));
351+
assertEquals("a selected tuple-array field must evaluate its write index exactly once",
352+
1, effectfulWrite.split("nextIndex\\(", -1).length - 1);
353+
assertTrue(readModifyWrite.contains("points_x["));
354+
assertFalse(readModifyWrite.contains("points_y["));
355+
assertFalse(readModifyWrite.contains("points_z["));
356+
assertTrue(nestedWrite.contains("segments_finish_y["));
357+
assertFalse(nestedWrite.contains("segments_start_"));
358+
assertFalse(nestedWrite.contains("segments_finish_x["));
359+
assertFalse(nestedWrite.contains("segments_finish_z["));
360+
assertTrue(memberWrite.contains("Entity_pos_z_storage["));
361+
assertFalse(memberWrite.contains("Entity_pos_x_storage["));
362+
assertFalse(memberWrite.contains("Entity_pos_y_storage["));
363+
assertTrue("whole-tuple assignment must still write x", wholeTupleWrite.contains("points_x["));
364+
assertTrue("whole-tuple assignment must still write y", wholeTupleWrite.contains("points_y["));
365+
assertTrue("whole-tuple assignment must still write z", wholeTupleWrite.contains("points_z["));
366+
assertFalse("selected tuple storage writes must not need discard helpers",
367+
plainWrite.contains("__wurst_tuple_discard_")
368+
|| effectfulWrite.contains("__wurst_tuple_discard_")
369+
|| readModifyWrite.contains("__wurst_tuple_discard_")
370+
|| nestedWrite.contains("__wurst_tuple_discard_")
371+
|| memberWrite.contains("__wurst_tuple_discard_"));
372+
}
373+
285374
@Test
286375
public void tupleMemberArrayAssignmentCapturesIndex() throws IOException {
287376
test().testLua(true).luaOnly(false).executeProg().lines(

0 commit comments

Comments
 (0)