[WHLSL] Enforce variable lifetimes
authorsbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 31 May 2019 04:31:54 +0000 (04:31 +0000)
committersbarati@apple.com <sbarati@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 31 May 2019 04:31:54 +0000 (04:31 +0000)
https://bugs.webkit.org/show_bug.cgi?id=195794
<rdar://problem/50746293>

Reviewed by Myles C. Maxfield.

Source/WebCore:

In WHLSL, each variable has global lifetime. So returning a pointer to a
local variable is a legitimate and well specified thing to do. Each local
variable has a unique place in memory. So, for example:

```
thread int* ptr() { int local; return &local; }
thread int* ptrPtr() { return ptr(); }
```

In the above program, ptr() must always return the same value
as ptrPtr(). So, the following would print "42":
```
thread int* p = ptrPtr();
*ptr() = 42;
print(*p);
```

To implement these semantics, this patch introduces a new pass which does the
following transformations:
- It notes every variable whose address is taken in the program.
- Each such variable gets defined as a field in a struct.
- Each function which is an entry point defines this struct.
- Each non entry point takes a pointer to this struct as its final parameter.
- Each call to a non-native function is rewritten to pass a pointer to the
  struct as the last call argument.
- Each variable reference to "x", where "x" ends up in the struct, is
  modified to instead be "struct->x". We store to "struct->x" after declaring
  "x". If "x" is a function parameter, we store to "struct->x" as the first
  thing we do in the function body.

Tests: webgpu/whlsl-ensure-proper-variable-lifetime-2.html
       webgpu/whlsl-ensure-proper-variable-lifetime-3.html
       webgpu/whlsl-ensure-proper-variable-lifetime.html
       webgpu/whlsl-return-local-variable.html

* Modules/webgpu/WHLSL/AST/WHLSLAST.h:
* Modules/webgpu/WHLSL/AST/WHLSLExpression.h:
(WebCore::WHLSL::AST::Expression::Expression):
(WebCore::WHLSL::AST::Expression::isGlobalVariableReference const):
(WebCore::WHLSL::AST::Expression::origin const): Deleted.
* Modules/webgpu/WHLSL/AST/WHLSLFunctionDeclaration.h:
(WebCore::WHLSL::AST::FunctionDeclaration::origin):
* Modules/webgpu/WHLSL/AST/WHLSLGlobalVariableReference.h: Added.
(WebCore::WHLSL::AST::GlobalVariableReference::GlobalVariableReference):
(WebCore::WHLSL::AST::GlobalVariableReference::structField):
(WebCore::WHLSL::AST::GlobalVariableReference::base):
* Modules/webgpu/WHLSL/AST/WHLSLStatement.h:
(WebCore::WHLSL::AST::Statement::Statement):
(WebCore::WHLSL::AST::Statement::isStatementList const):
(WebCore::WHLSL::AST::Statement::isWhileLoop const):
* Modules/webgpu/WHLSL/AST/WHLSLStatementList.h: Added.
(WebCore::WHLSL::AST::StatementList::StatementList):
(WebCore::WHLSL::AST::StatementList::statements):
* Modules/webgpu/WHLSL/AST/WHLSLValue.h:
(WebCore::WHLSL::AST::Value::Value):
(WebCore::WHLSL::AST::Value::origin const):
* Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h:
(WebCore::WHLSL::AST::VariableDeclaration::VariableDeclaration):
(WebCore::WHLSL::AST::VariableDeclaration::takeInitializer):
(WebCore::WHLSL::AST::VariableDeclaration::origin const): Deleted.
* Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h:
(WebCore::WHLSL::AST::VariableReference::wrap):
* Modules/webgpu/WHLSL/Metal/WHLSLFunctionWriter.cpp:
(WebCore::WHLSL::Metal::FunctionDefinitionWriter::visit):
* Modules/webgpu/WHLSL/WHLSLASTDumper.cpp:
(WebCore::WHLSL::ASTDumper::visit):
* Modules/webgpu/WHLSL/WHLSLASTDumper.h:
(WebCore::WHLSL::dumpASTNode):
(WebCore::WHLSL::dumpAST):
(WebCore::WHLSL::toString): Deleted.
* Modules/webgpu/WHLSL/WHLSLPrepare.cpp:
(WebCore::WHLSL::prepareShared):
* Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp: Added.
(WebCore::WHLSL::EscapedVariableCollector::takeEscapedVariables):
(WebCore::WHLSL::anonymousToken):
(WebCore::WHLSL::PreserveLifetimes::PreserveLifetimes):
(WebCore::WHLSL::PreserveLifetimes::makeStructVariableReference):
(WebCore::WHLSL::preserveVariableLifetimes):
* Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h: Added.
* Modules/webgpu/WHLSL/WHLSLVisitor.cpp:
(WebCore::WHLSL::Visitor::visit):
* Modules/webgpu/WHLSL/WHLSLVisitor.h:
* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:

Source/WTF:

* wtf/PrintStream.h:

LayoutTests:

* webgpu/whlsl-ensure-proper-variable-lifetime-2-expected.html: Added.
* webgpu/whlsl-ensure-proper-variable-lifetime-2.html: Added.
* webgpu/whlsl-ensure-proper-variable-lifetime-3-expected.html: Added.
* webgpu/whlsl-ensure-proper-variable-lifetime-3.html: Added.
* webgpu/whlsl-ensure-proper-variable-lifetime-expected.html: Added.
* webgpu/whlsl-ensure-proper-variable-lifetime.html: Added.
* webgpu/whlsl-return-local-variable-expected.html: Added.
* webgpu/whlsl-return-local-variable.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245945 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
LayoutTests/ChangeLog
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-return-local-variable-expected.html [new file with mode: 0644]
LayoutTests/webgpu/whlsl-return-local-variable.html [new file with mode: 0644]
Source/WTF/ChangeLog
Source/WTF/wtf/PrintStream.h
Source/WebCore/ChangeLog
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLAST.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLExpression.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLFunctionDeclaration.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLGlobalVariableReference.h [new file with mode: 0644]
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatement.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatementList.h [new file with mode: 0644]
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLValue.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h
Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h
Source/WebCore/Modules/webgpu/WHLSL/Metal/WHLSLFunctionWriter.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLASTDumper.h
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPrepare.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp [new file with mode: 0644]
Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h [new file with mode: 0644]
Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.cpp
Source/WebCore/Modules/webgpu/WHLSL/WHLSLVisitor.h
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj

index 279b28d..54cbf86 100644 (file)
@@ -1,3 +1,20 @@
+2019-05-30  Saam Barati  <sbarati@apple.com>
+
+        [WHLSL] Enforce variable lifetimes
+        https://bugs.webkit.org/show_bug.cgi?id=195794
+        <rdar://problem/50746293>
+
+        Reviewed by Myles C. Maxfield.
+
+        * webgpu/whlsl-ensure-proper-variable-lifetime-2-expected.html: Added.
+        * webgpu/whlsl-ensure-proper-variable-lifetime-2.html: Added.
+        * webgpu/whlsl-ensure-proper-variable-lifetime-3-expected.html: Added.
+        * webgpu/whlsl-ensure-proper-variable-lifetime-3.html: Added.
+        * webgpu/whlsl-ensure-proper-variable-lifetime-expected.html: Added.
+        * webgpu/whlsl-ensure-proper-variable-lifetime.html: Added.
+        * webgpu/whlsl-return-local-variable-expected.html: Added.
+        * webgpu/whlsl-return-local-variable.html: Added.
+
 2019-05-30  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r245890, 245887.
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2-expected.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-2.html
new file mode 100644 (file)
index 0000000..9e28813
--- /dev/null
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+struct VertexOut {
+    float4 position : SV_Position;
+    float shade : attribute(0);
+}
+
+float assignToPtr(thread float* ptr, float value) {
+    return *ptr = value;
+}
+
+thread float* realPtr(float value)
+{
+    return &value;
+}
+
+thread float* firstPtr(float value) {
+    return realPtr(value);
+}
+
+thread float* secondPtr(float value) {
+    return firstPtr(value);
+}
+
+vertex VertexOut vertexShader(float4 position : attribute(0), float shade : attribute(1)) {
+    VertexOut result;
+
+    thread float* ptrToShade = firstPtr(shade);
+    thread float* ptrToShade2 = secondPtr(1.0);
+
+    float dummy;
+    thread float* dummyPtr = &dummy;
+
+    dummy = assignToPtr(ptrToShade2, 0.0);
+    thread float* dummy2 = realPtr(shade);
+
+    result.position = position;
+
+    result.shade = *ptrToShade2;
+
+    return result;
+}
+
+fragment float4 fragmentShader(float shade : attribute(0)) : SV_Target 0 {
+    return float4(shade, shade, shade, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float", offset: 16};
+    const attributes = [attribute0, attribute1];
+    const input0 = {stride: 20, attributeSet: attributes};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBuffer0Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 5 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer0 = device.createBuffer(vertexBuffer0Descriptor);
+    const vertexBuffer0ArrayBuffer = await vertexBuffer0.mapWriteAsync();
+    const vertexBuffer0Float32Array = new Float32Array(vertexBuffer0ArrayBuffer);
+    vertexBuffer0Float32Array[0] = -0.5;
+    vertexBuffer0Float32Array[1] = -0.5;
+    vertexBuffer0Float32Array[2] = 1.0;
+    vertexBuffer0Float32Array[3] = 1.0;
+    vertexBuffer0Float32Array[4] = 1.0;
+
+    vertexBuffer0Float32Array[5] = -0.5;
+    vertexBuffer0Float32Array[6] = 0.5;
+    vertexBuffer0Float32Array[7] = 1.0;
+    vertexBuffer0Float32Array[8] = 1.0;
+    vertexBuffer0Float32Array[9] = 1.0;
+
+    vertexBuffer0Float32Array[10] = 0.5;
+    vertexBuffer0Float32Array[11] = -0.5;
+    vertexBuffer0Float32Array[12] = 1.0;
+    vertexBuffer0Float32Array[13] = 1.0;
+    vertexBuffer0Float32Array[14] = 1.0;
+
+    vertexBuffer0Float32Array[15] = 0.5;
+    vertexBuffer0Float32Array[16] = 0.5;
+    vertexBuffer0Float32Array[17] = 1.0;
+    vertexBuffer0Float32Array[18] = 1.0;
+    vertexBuffer0Float32Array[19] = 1.0;
+    vertexBuffer0.unmap();
+
+    const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer0], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3-expected.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-3.html
new file mode 100644 (file)
index 0000000..6554680
--- /dev/null
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+struct VertexOut {
+    float4 position : SV_Position;
+    float shade : attribute(0);
+}
+
+float assignToPtr(thread float* ptr, float value) {
+    return *ptr = value;
+}
+
+thread float* realPtr(float value)
+{
+    float v = value;
+    return &v;
+}
+
+thread float* firstPtr(float value) {
+    return realPtr(value);
+}
+
+thread float* secondPtr(float value) {
+    return firstPtr(value);
+}
+
+vertex VertexOut vertexShader(float4 position : attribute(0), float shade : attribute(1)) {
+    VertexOut result;
+
+    thread float* ptrToShade = firstPtr(shade);
+    thread float* ptrToShade2 = secondPtr(1.0);
+
+    float dummy;
+    thread float* dummyPtr = &dummy;
+
+    dummy = assignToPtr(ptrToShade2, 0.0);
+    thread float* dummy2 = realPtr(shade);
+
+    result.position = position;
+
+    result.shade = *ptrToShade2;
+
+    return result;
+}
+
+fragment float4 fragmentShader(float shade : attribute(0)) : SV_Target 0 {
+    return float4(shade, shade, shade, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float", offset: 16};
+    const attributes = [attribute0, attribute1];
+    const input0 = {stride: 20, attributeSet: attributes};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBuffer0Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 5 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer0 = device.createBuffer(vertexBuffer0Descriptor);
+    const vertexBuffer0ArrayBuffer = await vertexBuffer0.mapWriteAsync();
+    const vertexBuffer0Float32Array = new Float32Array(vertexBuffer0ArrayBuffer);
+    vertexBuffer0Float32Array[0] = -0.5;
+    vertexBuffer0Float32Array[1] = -0.5;
+    vertexBuffer0Float32Array[2] = 1.0;
+    vertexBuffer0Float32Array[3] = 1.0;
+    vertexBuffer0Float32Array[4] = 1.0;
+
+    vertexBuffer0Float32Array[5] = -0.5;
+    vertexBuffer0Float32Array[6] = 0.5;
+    vertexBuffer0Float32Array[7] = 1.0;
+    vertexBuffer0Float32Array[8] = 1.0;
+    vertexBuffer0Float32Array[9] = 1.0;
+
+    vertexBuffer0Float32Array[10] = 0.5;
+    vertexBuffer0Float32Array[11] = -0.5;
+    vertexBuffer0Float32Array[12] = 1.0;
+    vertexBuffer0Float32Array[13] = 1.0;
+    vertexBuffer0Float32Array[14] = 1.0;
+
+    vertexBuffer0Float32Array[15] = 0.5;
+    vertexBuffer0Float32Array[16] = 0.5;
+    vertexBuffer0Float32Array[17] = 1.0;
+    vertexBuffer0Float32Array[18] = 1.0;
+    vertexBuffer0Float32Array[19] = 1.0;
+    vertexBuffer0.unmap();
+
+    const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer0], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-expected.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime.html b/LayoutTests/webgpu/whlsl-ensure-proper-variable-lifetime.html
new file mode 100644 (file)
index 0000000..cb09944
--- /dev/null
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+struct VertexOut {
+    float4 position : SV_Position;
+    float shade : attribute(0);
+}
+
+float assignToPtr(thread float* ptr, float value) {
+    return *ptr = value;
+}
+
+thread float* realPtr(float value)
+{
+    return &value;
+}
+
+thread float* firstPtr(float value) {
+    return realPtr(value);
+}
+
+thread float* secondPtr(float value) {
+    return firstPtr(value);
+}
+
+vertex VertexOut vertexShader(float4 position : attribute(0), float shade : attribute(1)) {
+    VertexOut result;
+
+    thread float* ptrToShade = firstPtr(shade);
+    thread float* ptrToShade2 = secondPtr(1.0);
+
+    float dummy;
+    thread float* dummyPtr = &dummy;
+
+    dummy = assignToPtr(ptrToShade2, 0.0);
+    dummy = assignToPtr(ptrToShade, shade);
+
+    result.position = position;
+
+    result.shade = *ptrToShade2;
+
+    return result;
+}
+
+fragment float4 fragmentShader(float shade : attribute(0)) : SV_Target 0 {
+    return float4(shade, shade, shade, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float", offset: 16};
+    const attributes = [attribute0, attribute1];
+    const input0 = {stride: 20, attributeSet: attributes};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBuffer0Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 5 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer0 = device.createBuffer(vertexBuffer0Descriptor);
+    const vertexBuffer0ArrayBuffer = await vertexBuffer0.mapWriteAsync();
+    const vertexBuffer0Float32Array = new Float32Array(vertexBuffer0ArrayBuffer);
+    vertexBuffer0Float32Array[0] = -0.5;
+    vertexBuffer0Float32Array[1] = -0.5;
+    vertexBuffer0Float32Array[2] = 1.0;
+    vertexBuffer0Float32Array[3] = 1.0;
+    vertexBuffer0Float32Array[4] = 1.0;
+
+    vertexBuffer0Float32Array[5] = -0.5;
+    vertexBuffer0Float32Array[6] = 0.5;
+    vertexBuffer0Float32Array[7] = 1.0;
+    vertexBuffer0Float32Array[8] = 1.0;
+    vertexBuffer0Float32Array[9] = 1.0;
+
+    vertexBuffer0Float32Array[10] = 0.5;
+    vertexBuffer0Float32Array[11] = -0.5;
+    vertexBuffer0Float32Array[12] = 1.0;
+    vertexBuffer0Float32Array[13] = 1.0;
+    vertexBuffer0Float32Array[14] = 1.0;
+
+    vertexBuffer0Float32Array[15] = 0.5;
+    vertexBuffer0Float32Array[16] = 0.5;
+    vertexBuffer0Float32Array[17] = 1.0;
+    vertexBuffer0Float32Array[18] = 1.0;
+    vertexBuffer0Float32Array[19] = 1.0;
+    vertexBuffer0.unmap();
+
+    const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer0], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-return-local-variable-expected.html b/LayoutTests/webgpu/whlsl-return-local-variable-expected.html
new file mode 100644 (file)
index 0000000..f417050
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+async function start() {
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("2d");
+    context.fillStyle = "blue";
+    context.fillRect(0, 0, 400, 400);
+    context.fillStyle = "white";
+    context.fillRect(100, 100, 200, 200);
+}
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/webgpu/whlsl-return-local-variable.html b/LayoutTests/webgpu/whlsl-return-local-variable.html
new file mode 100644 (file)
index 0000000..664de10
--- /dev/null
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<canvas id="canvas" width="400" height="400"></canvas>
+<script>
+const shaderSource = `
+struct VertexOut {
+    float4 position : SV_Position;
+    float shade : attribute(0);
+}
+
+float copy(float v) {
+    float local = v;
+    return local;
+}
+
+vertex VertexOut vertexShader(float4 position : attribute(0), float shade : attribute(1)) {
+    VertexOut result;
+
+    result.position = position;
+    result.shade = copy(shade);
+
+    return result;
+}
+
+fragment float4 fragmentShader(float shade : attribute(0)) : SV_Target 0 {
+    return float4(shade, shade, shade, 1.0);
+}
+`;
+async function start() {
+    const adapter = await navigator.gpu.requestAdapter();
+    const device = await adapter.requestDevice();
+
+    const shaderModule = device.createShaderModule({code: shaderSource, isWHLSL: true});
+    const vertexStage = {module: shaderModule, entryPoint: "vertexShader"};
+    const fragmentStage = {module: shaderModule, entryPoint: "fragmentShader"};
+    const primitiveTopology = "triangle-strip";
+    const rasterizationState = {frontFace: "cw", cullMode: "none"};
+    const alphaBlend = {};
+    const colorBlend = {};
+    const colorStates = [{format: "rgba8unorm", alphaBlend, colorBlend, writeMask: 15}]; // GPUColorWriteBits.ALL
+    const depthStencilState = null;
+
+    const attribute0 = {shaderLocation: 0, format: "float4"};
+    const attribute1 = {shaderLocation: 1, format: "float", offset: 16};
+    const attributes = [attribute0, attribute1];
+    const input0 = {stride: 20, attributeSet: attributes};
+    const inputs = [input0];
+    const vertexInput = {vertexBuffers: inputs};
+
+    const bindGroupLayoutDescriptor = {bindings: [{binding: 0, visibility: 7, type: "uniform-buffer"}]};
+    const bindGroupLayout = device.createBindGroupLayout(bindGroupLayoutDescriptor);
+    const pipelineLayoutDescriptor = {bindGroupLayouts: [bindGroupLayout]};
+    const pipelineLayout = device.createPipelineLayout(pipelineLayoutDescriptor);
+
+    const renderPipelineDescriptor = {vertexStage, fragmentStage, primitiveTopology, rasterizationState, colorStates, depthStencilState, vertexInput, sampleCount: 1, layout: pipelineLayout};
+    const renderPipeline = device.createRenderPipeline(renderPipelineDescriptor);
+
+    const vertexBuffer0Descriptor = {size: Float32Array.BYTES_PER_ELEMENT * 5 * 4, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.MAP_WRITE};
+    const vertexBuffer0 = device.createBuffer(vertexBuffer0Descriptor);
+    const vertexBuffer0ArrayBuffer = await vertexBuffer0.mapWriteAsync();
+    const vertexBuffer0Float32Array = new Float32Array(vertexBuffer0ArrayBuffer);
+    vertexBuffer0Float32Array[0] = -0.5;
+    vertexBuffer0Float32Array[1] = -0.5;
+    vertexBuffer0Float32Array[2] = 1.0;
+    vertexBuffer0Float32Array[3] = 1.0;
+    vertexBuffer0Float32Array[4] = 1.0;
+
+    vertexBuffer0Float32Array[5] = -0.5;
+    vertexBuffer0Float32Array[6] = 0.5;
+    vertexBuffer0Float32Array[7] = 1.0;
+    vertexBuffer0Float32Array[8] = 1.0;
+    vertexBuffer0Float32Array[9] = 1.0;
+
+    vertexBuffer0Float32Array[10] = 0.5;
+    vertexBuffer0Float32Array[11] = -0.5;
+    vertexBuffer0Float32Array[12] = 1.0;
+    vertexBuffer0Float32Array[13] = 1.0;
+    vertexBuffer0Float32Array[14] = 1.0;
+
+    vertexBuffer0Float32Array[15] = 0.5;
+    vertexBuffer0Float32Array[16] = 0.5;
+    vertexBuffer0Float32Array[17] = 1.0;
+    vertexBuffer0Float32Array[18] = 1.0;
+    vertexBuffer0Float32Array[19] = 1.0;
+    vertexBuffer0.unmap();
+
+    const resourceBufferDescriptor = {size: Float32Array.BYTES_PER_ELEMENT, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.MAP_WRITE};
+    const resourceBuffer = device.createBuffer(resourceBufferDescriptor);
+    const resourceBufferArrayBuffer = await resourceBuffer.mapWriteAsync();
+    const resourceBufferFloat32Array = new Float32Array(resourceBufferArrayBuffer);
+    resourceBufferFloat32Array[0] = 1;
+    resourceBuffer.unmap();
+
+    const bufferBinding = {buffer: resourceBuffer, size: 4};
+    const bindGroupBinding = {binding: 0, resource: bufferBinding};
+    const bindGroupDescriptor = {layout: bindGroupLayout, bindings: [bindGroupBinding]};
+    const bindGroup = device.createBindGroup(bindGroupDescriptor);
+
+    const canvas = document.getElementById("canvas");
+    const context = canvas.getContext("gpu");
+    const swapChainDescriptor = {device, format: "bgra8unorm"};
+    const swapChain = context.configureSwapChain(swapChainDescriptor);
+    const outputTexture = swapChain.getCurrentTexture();
+    const outputTextureView = outputTexture.createDefaultView();
+
+    const commandEncoder = device.createCommandEncoder(); // {}
+    const red = {r: 0, g: 0, b: 1, a: 1};
+    const colorAttachments = [{attachment: outputTextureView, resolveTarget: null, loadOp: "clear", storeOp: "store", clearColor: red}];
+    const depthStencilAttachment = null;
+    const renderPassDescriptor = {colorAttachments, depthStencilAttachment};
+    const renderPassEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+    renderPassEncoder.setPipeline(renderPipeline);
+    renderPassEncoder.setBindGroup(0, bindGroup);
+    renderPassEncoder.setVertexBuffers(0, [vertexBuffer0], [0]);
+    renderPassEncoder.draw(4, 1, 0, 0);
+    renderPassEncoder.endPass();
+    const commandBuffer = commandEncoder.finish();
+    device.getQueue().submit([commandBuffer]);
+
+    if (window.testRunner)
+        testRunner.notifyDone();
+}
+if (window.testRunner)
+    testRunner.waitUntilDone();
+window.addEventListener("load", start);
+</script>
+</body>
+</html>
index 1c5f5bc..a5cad1d 100644 (file)
@@ -1,3 +1,13 @@
+2019-05-30  Saam Barati  <sbarati@apple.com>
+
+        [WHLSL] Enforce variable lifetimes
+        https://bugs.webkit.org/show_bug.cgi?id=195794
+        <rdar://problem/50746293>
+
+        Reviewed by Myles C. Maxfield.
+
+        * wtf/PrintStream.h:
+
 2019-05-30  Keith Rollin  <krollin@apple.com>
 
         Fix yet more deprecated uses of -[UIApplication interfaceOrientation]
index 0249490..5c9fbdb 100644 (file)
@@ -132,7 +132,7 @@ void printInternal(PrintStream& out, const T& value)
 #define MAKE_PRINT_ADAPTOR(Name, Type, function) \
     class Name {                                 \
     public:                                      \
-        Name(const Type& value)                  \
+        Name(Type value)                         \
             : m_value(value)                     \
         {                                        \
         }                                        \
index 10e75b0..3c8d9d0 100644 (file)
@@ -1,3 +1,96 @@
+2019-05-30  Saam Barati  <sbarati@apple.com>
+
+        [WHLSL] Enforce variable lifetimes
+        https://bugs.webkit.org/show_bug.cgi?id=195794
+        <rdar://problem/50746293>
+
+        Reviewed by Myles C. Maxfield.
+
+        In WHLSL, each variable has global lifetime. So returning a pointer to a
+        local variable is a legitimate and well specified thing to do. Each local
+        variable has a unique place in memory. So, for example:
+        
+        ```
+        thread int* ptr() { int local; return &local; }
+        thread int* ptrPtr() { return ptr(); }
+        ```
+        
+        In the above program, ptr() must always return the same value
+        as ptrPtr(). So, the following would print "42":
+        ```
+        thread int* p = ptrPtr();
+        *ptr() = 42;
+        print(*p);
+        ```
+        
+        To implement these semantics, this patch introduces a new pass which does the
+        following transformations:
+        - It notes every variable whose address is taken in the program.
+        - Each such variable gets defined as a field in a struct.
+        - Each function which is an entry point defines this struct.
+        - Each non entry point takes a pointer to this struct as its final parameter.
+        - Each call to a non-native function is rewritten to pass a pointer to the
+          struct as the last call argument.
+        - Each variable reference to "x", where "x" ends up in the struct, is
+          modified to instead be "struct->x". We store to "struct->x" after declaring
+          "x". If "x" is a function parameter, we store to "struct->x" as the first
+          thing we do in the function body.
+
+        Tests: webgpu/whlsl-ensure-proper-variable-lifetime-2.html
+               webgpu/whlsl-ensure-proper-variable-lifetime-3.html
+               webgpu/whlsl-ensure-proper-variable-lifetime.html
+               webgpu/whlsl-return-local-variable.html
+
+        * Modules/webgpu/WHLSL/AST/WHLSLAST.h:
+        * Modules/webgpu/WHLSL/AST/WHLSLExpression.h:
+        (WebCore::WHLSL::AST::Expression::Expression):
+        (WebCore::WHLSL::AST::Expression::isGlobalVariableReference const):
+        (WebCore::WHLSL::AST::Expression::origin const): Deleted.
+        * Modules/webgpu/WHLSL/AST/WHLSLFunctionDeclaration.h:
+        (WebCore::WHLSL::AST::FunctionDeclaration::origin):
+        * Modules/webgpu/WHLSL/AST/WHLSLGlobalVariableReference.h: Added.
+        (WebCore::WHLSL::AST::GlobalVariableReference::GlobalVariableReference):
+        (WebCore::WHLSL::AST::GlobalVariableReference::structField):
+        (WebCore::WHLSL::AST::GlobalVariableReference::base):
+        * Modules/webgpu/WHLSL/AST/WHLSLStatement.h:
+        (WebCore::WHLSL::AST::Statement::Statement):
+        (WebCore::WHLSL::AST::Statement::isStatementList const):
+        (WebCore::WHLSL::AST::Statement::isWhileLoop const):
+        * Modules/webgpu/WHLSL/AST/WHLSLStatementList.h: Added.
+        (WebCore::WHLSL::AST::StatementList::StatementList):
+        (WebCore::WHLSL::AST::StatementList::statements):
+        * Modules/webgpu/WHLSL/AST/WHLSLValue.h:
+        (WebCore::WHLSL::AST::Value::Value):
+        (WebCore::WHLSL::AST::Value::origin const):
+        * Modules/webgpu/WHLSL/AST/WHLSLVariableDeclaration.h:
+        (WebCore::WHLSL::AST::VariableDeclaration::VariableDeclaration):
+        (WebCore::WHLSL::AST::VariableDeclaration::takeInitializer):
+        (WebCore::WHLSL::AST::VariableDeclaration::origin const): Deleted.
+        * Modules/webgpu/WHLSL/AST/WHLSLVariableReference.h:
+        (WebCore::WHLSL::AST::VariableReference::wrap):
+        * Modules/webgpu/WHLSL/Metal/WHLSLFunctionWriter.cpp:
+        (WebCore::WHLSL::Metal::FunctionDefinitionWriter::visit):
+        * Modules/webgpu/WHLSL/WHLSLASTDumper.cpp:
+        (WebCore::WHLSL::ASTDumper::visit):
+        * Modules/webgpu/WHLSL/WHLSLASTDumper.h:
+        (WebCore::WHLSL::dumpASTNode):
+        (WebCore::WHLSL::dumpAST):
+        (WebCore::WHLSL::toString): Deleted.
+        * Modules/webgpu/WHLSL/WHLSLPrepare.cpp:
+        (WebCore::WHLSL::prepareShared):
+        * Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp: Added.
+        (WebCore::WHLSL::EscapedVariableCollector::takeEscapedVariables):
+        (WebCore::WHLSL::anonymousToken):
+        (WebCore::WHLSL::PreserveLifetimes::PreserveLifetimes):
+        (WebCore::WHLSL::PreserveLifetimes::makeStructVariableReference):
+        (WebCore::WHLSL::preserveVariableLifetimes):
+        * Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h: Added.
+        * Modules/webgpu/WHLSL/WHLSLVisitor.cpp:
+        (WebCore::WHLSL::Visitor::visit):
+        * Modules/webgpu/WHLSL/WHLSLVisitor.h:
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+
 2019-05-30  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r245890, 245887.
index da2e648..f513c70 100644 (file)
@@ -57,6 +57,7 @@
 #include "WHLSLFunctionAttribute.h"
 #include "WHLSLFunctionDeclaration.h"
 #include "WHLSLFunctionDefinition.h"
+#include "WHLSLGlobalVariableReference.h"
 #include "WHLSLIfStatement.h"
 #include "WHLSLIndexExpression.h"
 #include "WHLSLIntegerLiteral.h"
@@ -84,6 +85,7 @@
 #include "WHLSLSpecializationConstantSemantic.h"
 #include "WHLSLStageInOutSemantic.h"
 #include "WHLSLStatement.h"
+#include "WHLSLStatementList.h"
 #include "WHLSLStructureDefinition.h"
 #include "WHLSLStructureElement.h"
 #include "WHLSLSwitchCase.h"
index 7677b99..2984c04 100644 (file)
@@ -41,9 +41,10 @@ namespace WHLSL {
 namespace AST {
 
 class Expression : public Value {
+    using Base = Value;
 public:
     Expression(Lexer::Token&& origin)
-        : m_origin(WTFMove(origin))
+        : Base(WTFMove(origin))
     {
     }
 
@@ -55,8 +56,6 @@ public:
     Expression& operator=(const Expression&) = delete;
     Expression& operator=(Expression&&) = default;
 
-    const Lexer::Token& origin() const { return m_origin; }
-
     UnnamedType* maybeResolvedType() { return m_type ? &*m_type : nullptr; }
 
     UnnamedType& resolvedType()
@@ -91,6 +90,7 @@ public:
     virtual bool isCommaExpression() const { return false; }
     virtual bool isDereferenceExpression() const { return false; }
     virtual bool isDotExpression() const { return false; }
+    virtual bool isGlobalVariableReference() const { return false; }
     virtual bool isFloatLiteral() const { return false; }
     virtual bool isIndexExpression() const { return false; }
     virtual bool isIntegerLiteral() const { return false; }
@@ -107,7 +107,6 @@ public:
     virtual bool isEnumerationMemberLiteral() const { return false; }
 
 private:
-    Lexer::Token m_origin;
     Optional<UniqueRef<UnnamedType>> m_type;
     Optional<TypeAnnotation> m_typeAnnotation;
 };
index c15466a..52ab6a5 100644 (file)
@@ -76,6 +76,7 @@ public:
     VariableDeclarations& parameters() { return m_parameters; }
     Optional<Semantic>& semantic() { return m_semantic; }
     bool isOperator() const { return m_isOperator; }
+    Lexer::Token origin() { return m_origin; }
 
 private:
     Lexer::Token m_origin;
diff --git a/Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLGlobalVariableReference.h b/Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLGlobalVariableReference.h
new file mode 100644 (file)
index 0000000..b2b5f48
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBGPU)
+
+#include "WHLSLLexer.h"
+#include "WHLSLStructureElement.h"
+#include <wtf/UniqueRef.h>
+
+namespace WebCore {
+
+namespace WHLSL {
+
+namespace AST {
+
+class GlobalVariableReference : public Expression {
+public:
+    GlobalVariableReference(Lexer::Token&& origin, UniqueRef<Expression>&& base, StructureElement* structField)
+        : Expression(WTFMove(origin))
+        , m_base(WTFMove(base))
+        , m_structField(*structField)
+    {
+        ASSERT(structField);
+    }
+
+    virtual ~GlobalVariableReference() = default;
+    bool isGlobalVariableReference() const override { return true; }
+    StructureElement& structField() { return m_structField; }
+
+    Expression& base() { return m_base.get(); }
+
+private:
+    UniqueRef<Expression> m_base;
+    StructureElement& m_structField;
+};
+
+} // namespace AST
+
+}
+
+}
+
+SPECIALIZE_TYPE_TRAITS_WHLSL_EXPRESSION(GlobalVariableReference, isGlobalVariableReference())
+
+#endif
index b56c19c..57f1bf3 100644 (file)
@@ -38,9 +38,10 @@ namespace WHLSL {
 namespace AST {
 
 class Statement : public Value {
+    using Base = Value;
 public:
     Statement(Lexer::Token&& origin)
-        : m_origin(WTFMove(origin))
+        : Base(WTFMove(origin))
     {
     }
 
@@ -58,14 +59,12 @@ public:
     virtual bool isForLoop() const { return false; }
     virtual bool isIfStatement() const { return false; }
     virtual bool isReturn() const { return false; }
+    virtual bool isStatementList() const { return false; }
     virtual bool isSwitchCase() const { return false; }
     virtual bool isSwitchStatement() const { return false; }
     virtual bool isTrap() const { return false; }
     virtual bool isVariableDeclarationsStatement() const { return false; }
     virtual bool isWhileLoop() const { return false; }
-
-private:
-    Lexer::Token m_origin;
 };
 
 using Statements = Vector<UniqueRef<Statement>>;
diff --git a/Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatementList.h b/Source/WebCore/Modules/webgpu/WHLSL/AST/WHLSLStatementList.h
new file mode 100644 (file)
index 0000000..3ec6f42
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBGPU)
+
+#include "WHLSLLexer.h"
+#include "WHLSLStatement.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+namespace WHLSL {
+
+namespace AST {
+
+class StatementList : public Statement {
+    using Base = Statement;
+public:
+    StatementList(Lexer::Token&& origin, Statements&& statements)
+        : Base(WTFMove(origin))
+        , m_statements(WTFMove(statements))
+    { }
+
+    virtual ~StatementList() = default;
+
+    Statements& statements() { return m_statements; }
+
+    bool isStatementList() const override { return true; }
+
+private:
+    Statements m_statements;
+};
+
+} // namespace AST
+
+}
+
+}
+
+SPECIALIZE_TYPE_TRAITS_WHLSL_STATEMENT(StatementList, isStatementList())
+
+#endif
index ffd9c8e..c4119b1 100644 (file)
@@ -37,7 +37,8 @@ namespace AST {
 
 class Value : public Node {
 public:
-    Value()
+    Value(Lexer::Token&& origin)
+        : m_origin(WTFMove(origin))
     {
     }
 
@@ -49,7 +50,10 @@ public:
     Value& operator=(const Value&) = default;
     Value& operator=(Value&&) = default;
 
-private:
+    Lexer::Token origin() const { return m_origin; }
+
+protected:
+    Lexer::Token m_origin;
 };
 
 } // namespace AST
index e50aaad..d3c64a3 100644 (file)
@@ -45,9 +45,10 @@ namespace WHLSL {
 namespace AST {
 
 class VariableDeclaration : public Value {
+    using Base = Value;
 public:
     VariableDeclaration(Lexer::Token&& origin, Qualifiers&& qualifiers, Optional<UniqueRef<UnnamedType>>&& type, String&& name, Optional<Semantic>&& semantic, Optional<UniqueRef<Expression>>&& initializer)
-        : m_origin(WTFMove(origin))
+        : Base(WTFMove(origin))
         , m_qualifiers(WTFMove(qualifiers))
         , m_type(WTFMove(type))
         , m_name(WTFMove(name))
@@ -61,7 +62,6 @@ public:
     VariableDeclaration(const VariableDeclaration&) = delete;
     VariableDeclaration(VariableDeclaration&&) = default;
 
-    const Lexer::Token& origin() const { return m_origin; }
     String& name() { return m_name; }
 
     const Optional<UniqueRef<UnnamedType>>& type() const { return m_type; } // Anonymous variables inside ReadModifyWriteExpressions have their type set by the type checker.
@@ -69,9 +69,9 @@ public:
     Optional<Semantic>& semantic() { return m_semantic; }
     Expression* initializer() { return m_initializer ? &*m_initializer : nullptr; }
     bool isAnonymous() const { return m_name.isNull(); }
+    Optional<UniqueRef<Expression>> takeInitializer() { return WTFMove(m_initializer); }
 
 private:
-    Lexer::Token m_origin;
     Qualifiers m_qualifiers;
     Optional<UniqueRef<UnnamedType>> m_type;
     String m_name;
index 69e79ad..ade40e7 100644 (file)
@@ -55,6 +55,7 @@ public:
     {
         VariableReference result(Lexer::Token(variableDeclaration.origin()));
         result.m_variable = &variableDeclaration;
+        result.m_name = variableDeclaration.name();
         return result;
     }
 
index 543f8c0..d236b8d 100644 (file)
 #if ENABLE(WEBGPU)
 
 #include "NotImplemented.h"
-#include "WHLSLArrayReferenceType.h"
-#include "WHLSLArrayType.h"
-#include "WHLSLAssignmentExpression.h"
-#include "WHLSLBooleanLiteral.h"
-#include "WHLSLBuiltInSemantic.h"
-#include "WHLSLCallExpression.h"
-#include "WHLSLCommaExpression.h"
-#include "WHLSLDereferenceExpression.h"
-#include "WHLSLDoWhileLoop.h"
-#include "WHLSLEffectfulExpressionStatement.h"
-#include "WHLSLEntryPointScaffolding.h"
-#include "WHLSLEntryPointType.h"
-#include "WHLSLFloatLiteral.h"
-#include "WHLSLForLoop.h"
-#include "WHLSLFunctionDeclaration.h"
-#include "WHLSLFunctionDefinition.h"
-#include "WHLSLIfStatement.h"
-#include "WHLSLIntegerLiteral.h"
-#include "WHLSLLogicalExpression.h"
-#include "WHLSLLogicalNotExpression.h"
-#include "WHLSLMakeArrayReferenceExpression.h"
-#include "WHLSLMakePointerExpression.h"
-#include "WHLSLNativeFunctionDeclaration.h"
+#include "WHLSLAST.h"
 #include "WHLSLNativeFunctionWriter.h"
-#include "WHLSLNativeTypeDeclaration.h"
-#include "WHLSLPointerType.h"
 #include "WHLSLProgram.h"
-#include "WHLSLReturn.h"
-#include "WHLSLSwitchCase.h"
-#include "WHLSLSwitchStatement.h"
-#include "WHLSLTernaryExpression.h"
 #include "WHLSLTypeNamer.h"
-#include "WHLSLUnsignedIntegerLiteral.h"
-#include "WHLSLVariableDeclaration.h"
-#include "WHLSLVariableDeclarationsStatement.h"
-#include "WHLSLVariableReference.h"
 #include "WHLSLVisitor.h"
-#include "WHLSLWhileLoop.h"
 #include <wtf/HashMap.h>
 #include <wtf/text/StringBuilder.h>
 
@@ -156,6 +123,7 @@ protected:
     void visit(AST::EnumerationMemberLiteral&) override;
     void visit(AST::Expression&) override;
     void visit(AST::DotExpression&) override;
+    void visit(AST::GlobalVariableReference&) override;
     void visit(AST::IndexExpression&) override;
     void visit(AST::PropertyAccessExpression&) override;
     void visit(AST::VariableDeclaration&) override;
@@ -446,6 +414,15 @@ void FunctionDefinitionWriter::visit(AST::DotExpression&)
     m_stack.append("dummy");
 }
 
+void FunctionDefinitionWriter::visit(AST::GlobalVariableReference& globalVariableReference)
+{
+    auto variableName = generateNextVariableName();
+    auto mangledTypeName = m_typeNamer.mangledNameForType(globalVariableReference.resolvedType());
+    checkErrorAndVisit(globalVariableReference.base());
+    m_stringBuilder.append(makeString("thread ", mangledTypeName, "& ", variableName, " = ", m_stack.takeLast(), "->", m_typeNamer.mangledNameForStructureElement(globalVariableReference.structField()), ";\n"));
+    m_stack.append(variableName);
+}
+
 void FunctionDefinitionWriter::visit(AST::IndexExpression&)
 {
     // This should be lowered already.
index 3eb153c..b7a9933 100644 (file)
@@ -372,6 +372,17 @@ void ASTDumper::visit(AST::Statement& statement)
     Base::visit(statement);
 }
 
+void ASTDumper::visit(AST::StatementList& statementList)
+{
+    bool once = false;
+    for (auto& statement : statementList.statements()) {
+        if (once)
+            m_out.print(";\n", m_indent);
+        once = true;
+        visit(statement);
+    }
+}
+
 void ASTDumper::visit(AST::Break&)
 {
     m_out.print("break");
@@ -441,6 +452,12 @@ void ASTDumper::visit(AST::DotExpression& dotExpression)
     m_out.print(".", dotExpression.fieldName());
 }
 
+void ASTDumper::visit(AST::GlobalVariableReference& globalVariableReference)
+{
+    visit(globalVariableReference.base());
+    m_out.print("=>", globalVariableReference.structField().name());
+}
+
 void ASTDumper::visit(AST::IndexExpression& indexExpression)
 {
     visit(static_cast<AST::PropertyAccessExpression&>(indexExpression));
@@ -529,7 +546,12 @@ void ASTDumper::visit(AST::VariableDeclaration& variableDeclaration)
         visit(*variableDeclaration.type());
         m_out.print(" ");
     }
-    m_out.print(variableDeclaration.name());
+
+    if (variableDeclaration.name().isEmpty())
+        m_out.print("$", RawPointer(&variableDeclaration));
+    else
+        m_out.print(variableDeclaration.name());
+
     if (variableDeclaration.semantic())
         visit(*variableDeclaration.semantic());
     if (variableDeclaration.initializer()) {
index 7ea1b65..93eccd2 100644 (file)
@@ -44,7 +44,6 @@ public:
 
     String toString() { return m_out.toString(); }
 
-private:
     void visit(AST::UnnamedType&) override;
     void visit(AST::NamedType&) override;
     void visit(AST::TypeDefinition&) override;
@@ -82,12 +81,14 @@ private:
     void visit(AST::FunctionAttribute&) override;
     void visit(AST::NumThreadsFunctionAttribute&) override;
     void visit(AST::Block&) override;
+    void visit(AST::StatementList&) override;
     void visit(AST::Statement&) override;
     void visit(AST::Break&) override;
     void visit(AST::Continue&) override;
     void visit(AST::DoWhileLoop&) override;
     void visit(AST::Expression&) override;
     void visit(AST::DotExpression&) override;
+    void visit(AST::GlobalVariableReference&) override;
     void visit(AST::IndexExpression&) override;
     void visit(AST::PropertyAccessExpression&) override;
     void visit(AST::EffectfulExpressionStatement&) override;
@@ -113,6 +114,7 @@ private:
     void visit(AST::TernaryExpression&) override;
     void visit(AST::VariableReference&) override;
 
+private:
     struct Indent {
         Indent(ASTDumper& dumper)
             : m_scope(dumper.m_indent, dumper.m_indent + "   ")
@@ -128,16 +130,23 @@ private:
     String m_indent;
 };
 
-static ALWAYS_INLINE String toString(Program& program)
+template <typename T>
+ALWAYS_INLINE void dumpASTNode(PrintStream& out, T& value)
 {
     ASTDumper dumper;
-    dumper.visit(program);
-    return dumper.toString();
+    dumper.visit(value);
+    out.print(dumper.toString());
 }
+MAKE_PRINT_ADAPTOR(ExpressionDumper, AST::Expression&, dumpASTNode);
+MAKE_PRINT_ADAPTOR(StatementDumper, AST::Statement&, dumpASTNode);
+MAKE_PRINT_ADAPTOR(ProgramDumper, Program&, dumpASTNode);
+MAKE_PRINT_ADAPTOR(StructureDefinitionDumper, AST::StructureDefinition&, dumpASTNode);
+MAKE_PRINT_ADAPTOR(FunctionDefinitionDumper, AST::FunctionDefinition&, dumpASTNode);
+
 
 static ALWAYS_INLINE void dumpAST(Program& program)
 {
-    dataLogLn(toString(program));
+    dataLogLn(ProgramDumper(program));
 }
 
 } // namespace WHLSL
index 95f7ca2..e3535a4 100644 (file)
@@ -37,6 +37,7 @@
 #include "WHLSLMetalCodeGenerator.h"
 #include "WHLSLNameResolver.h"
 #include "WHLSLParser.h"
+#include "WHLSLPreserveVariableLifetimes.h"
 #include "WHLSLProgram.h"
 #include "WHLSLPropertyResolver.h"
 #include "WHLSLRecursionChecker.h"
@@ -56,7 +57,9 @@ namespace WHLSL {
 
 static constexpr bool dumpASTBeforeEachPass = false;
 static constexpr bool dumpASTAfterParsing = false;
-static constexpr bool dumpASTAtEnd = false;
+static constexpr bool dumpASTAtEnd = true;
+static constexpr bool alwaysDumpPassFailures = false;
+static constexpr bool dumpPassFailure = dumpASTBeforeEachPass || dumpASTAfterParsing || dumpASTAtEnd || alwaysDumpPassFailures;
 
 static bool dumpASTIfNeeded(bool shouldDump, Program& program, const char* message)
 {
@@ -87,8 +90,11 @@ static bool dumpASTAtEndIfNeeded(Program& program)
 #define RUN_PASS(pass, ...) \
     do { \
         dumpASTBetweenEachPassIfNeeded(program, "AST before " # pass); \
-        if (!pass(__VA_ARGS__)) \
+        if (!pass(__VA_ARGS__)) { \
+            if (dumpPassFailure) \
+                dataLogLn("failed pass: " # pass); \
             return WTF::nullopt; \
+        } \
     } while (0)
     
 
@@ -123,6 +129,7 @@ static Optional<Program> prepareShared(String& whlslSource)
     RUN_PASS(checkStatementBehavior, program);
     RUN_PASS(checkRecursion, program);
     RUN_PASS(checkFunctionStages, program);
+    preserveVariableLifetimes(program);
 
     dumpASTAtEndIfNeeded(program);
 
diff --git a/Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp b/Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp
new file mode 100644 (file)
index 0000000..610b5fe
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WHLSLPreserveVariableLifetimes.h"
+
+#if ENABLE(WEBGPU)
+
+#include "WHLSLAST.h"
+#include "WHLSLASTDumper.h"
+#include "WHLSLVisitor.h"
+
+namespace WebCore {
+
+namespace WHLSL {
+
+// This pass works by ensuring proper variable lifetimes. In WHLSL, each variable
+// has global lifetime. So returning a pointer to a local variable is a totally
+// legitimate and well-specified thing to do.
+//
+// We implement this by:
+// - We note every variable whose address we take.
+// - Each such variable gets defined as a field in a struct.
+// - Each function which is an entry point defines this struct.
+// - Each non entry point takes a pointer to this struct as its final parameter.
+// - Each call to a non-native function is rewritten to pass a pointer to the
+//   struct as the last call argument.
+// - Each variable reference to "x", where "x" ends up in the struct, is
+//   modified to instead be "struct->x". We store to "struct->x" after declaring
+//   "x". If "x" is a function parameter, we store to "struct->x" as the first
+//   thing we do in the function body.
+
+class EscapedVariableCollector : public Visitor {
+    using Base = Visitor;
+public:
+    void visit(AST::MakePointerExpression& makePointerExpression) override
+    {
+        if (!is<AST::VariableReference>(makePointerExpression.leftValue())) {
+            // FIXME: Are we missing any interesting productions here?
+            // https://bugs.webkit.org/show_bug.cgi?id=198311
+            Base::visit(makePointerExpression.leftValue());
+            return;
+        }
+
+        auto& variableReference = downcast<AST::VariableReference>(makePointerExpression.leftValue());
+        auto* variable = variableReference.variable();
+        ASSERT(variable);
+        // FIXME: We could skip this if we mark all internal variables with a bit, since we
+        // never make any internal variable escape the current scope it is defined in:
+        // https://bugs.webkit.org/show_bug.cgi?id=198383
+        m_escapedVariables.add(variable, makeString("_", variable->name(), "_", m_count++));
+    }
+
+    HashMap<AST::VariableDeclaration*, String> takeEscapedVariables() { return WTFMove(m_escapedVariables); }
+
+private:
+    size_t m_count { 1 };
+    HashMap<AST::VariableDeclaration*, String> m_escapedVariables;
+};
+
+static ALWAYS_INLINE Lexer::Token anonymousToken(Lexer::Token::Type type)
+{
+    return Lexer::Token { { }, 0, type };
+}
+
+class PreserveLifetimes : public Visitor {
+    using Base = Visitor;
+public:
+    PreserveLifetimes(UniqueRef<AST::TypeReference>& structType, const HashMap<AST::VariableDeclaration*, AST::StructureElement*>& variableMapping)
+        : m_structType(structType)
+        , m_pointerToStructType(makeUniqueRef<AST::PointerType>(anonymousToken(Lexer::Token::Type::Identifier), AST::AddressSpace::Thread, m_structType->clone()))
+        , m_variableMapping(variableMapping)
+    { }
+
+    UniqueRef<AST::VariableReference> makeStructVariableReference()
+    {
+        auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(*m_structVariable));
+        structVariableReference->setType(m_structVariable->type()->clone());
+        structVariableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+        return structVariableReference;
+    }
+
+    UniqueRef<AST::AssignmentExpression> assignVariableIntoStruct(AST::VariableDeclaration& variable, AST::StructureElement* element)
+    {
+        auto lhs = makeUniqueRef<AST::GlobalVariableReference>(variable.origin(), makeStructVariableReference(), element);
+        lhs->setType(variable.type()->clone());
+        lhs->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+
+        auto rhs = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(variable));
+        rhs->setType(variable.type()->clone());
+        rhs->setTypeAnnotation(AST::RightValue());
+
+        auto assignment = makeUniqueRef<AST::AssignmentExpression>(variable.origin(), WTFMove(lhs), WTFMove(rhs));
+        assignment->setType(variable.type()->clone());
+        assignment->setTypeAnnotation(AST::RightValue());
+
+        return assignment;
+    }
+
+    void visit(AST::FunctionDefinition& functionDefinition) override
+    {
+        bool isEntryPoint = !!functionDefinition.entryPointType();
+        if (isEntryPoint) {
+            auto structVariableDeclaration = makeUniqueRef<AST::VariableDeclaration>(functionDefinition.origin(), AST::Qualifiers(),
+                m_structType->clone(), String(), WTF::nullopt, WTF::nullopt);
+
+            auto structVariableReference = makeUniqueRef<AST::VariableReference>(AST::VariableReference::wrap(structVariableDeclaration));
+            structVariableReference->setType(m_structType->clone());
+            structVariableReference->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+
+            AST::VariableDeclarations structVariableDeclarations;
+            structVariableDeclarations.append(WTFMove(structVariableDeclaration));
+            auto structDeclarationStatement = makeUniqueRef<AST::VariableDeclarationsStatement>(functionDefinition.origin(), WTFMove(structVariableDeclarations));
+
+            auto makePointerExpression = makeUniqueRef<AST::MakePointerExpression>(functionDefinition.origin(), WTFMove(structVariableReference));
+            makePointerExpression->setType(m_pointerToStructType->clone());
+            makePointerExpression->setTypeAnnotation(AST::LeftValue { AST::AddressSpace::Thread });
+
+            auto pointerDeclaration = makeUniqueRef<AST::VariableDeclaration>(functionDefinition.origin(), AST::Qualifiers(),
+                m_pointerToStructType->clone(), "wrapper"_s, WTF::nullopt, Optional<UniqueRef<AST::Expression>>(WTFMove(makePointerExpression)));
+            m_structVariable = &pointerDeclaration;
+
+            AST::VariableDeclarations pointerVariableDeclarations;
+            pointerVariableDeclarations.append(WTFMove(pointerDeclaration));
+            auto pointerDeclarationStatement = makeUniqueRef<AST::VariableDeclarationsStatement>(functionDefinition.origin(), WTFMove(pointerVariableDeclarations));
+
+            functionDefinition.block().statements().insert(0, WTFMove(structDeclarationStatement));
+            functionDefinition.block().statements().insert(1, WTFMove(pointerDeclarationStatement));
+        } else {
+            auto pointerDeclaration = makeUniqueRef<AST::VariableDeclaration>(functionDefinition.origin(), AST::Qualifiers(),
+                m_pointerToStructType->clone(), "wrapper"_s, WTF::nullopt, WTF::nullopt);
+            m_structVariable = &pointerDeclaration;
+            functionDefinition.parameters().append(WTFMove(pointerDeclaration));
+        }
+
+        Base::visit(functionDefinition);
+
+        for (auto& parameter : functionDefinition.parameters()) {
+            auto iter = m_variableMapping.find(&parameter);
+            if (iter == m_variableMapping.end())
+                continue;
+
+            functionDefinition.block().statements().insert(isEntryPoint ? 2 : 0,
+                makeUniqueRef<AST::EffectfulExpressionStatement>(assignVariableIntoStruct(parameter, iter->value)));
+        }
+
+        // Inner functions are not allowed in WHLSL. So this is fine.
+        m_structVariable = nullptr;
+    }
+
+    void visit(AST::CallExpression& callExpression) override
+    {
+        RELEASE_ASSERT(m_structVariable);
+
+        Base::visit(callExpression);
+
+        // This works because it's illegal to call an entrypoint. Therefore, we can only
+        // call functions where we've already appended this struct as its final parameter.
+        if (!callExpression.function()->isNativeFunctionDeclaration())
+            callExpression.arguments().append(makeStructVariableReference());
+    }
+
+    void visit(AST::VariableReference& variableReference) override
+    {
+        RELEASE_ASSERT(m_structVariable);
+
+        auto iter = m_variableMapping.find(variableReference.variable());
+        if (iter == m_variableMapping.end())
+            return;
+
+        auto type = variableReference.variable()->type()->clone();
+        AST::TypeAnnotation typeAnnotation = variableReference.typeAnnotation();
+        auto* internalField = AST::replaceWith<AST::GlobalVariableReference>(variableReference, variableReference.origin(), makeStructVariableReference(), iter->value);
+        internalField->setType(WTFMove(type));
+        internalField->setTypeAnnotation(WTFMove(typeAnnotation));
+    }
+
+    void visit(AST::VariableDeclarationsStatement& variableDeclarationsStatement) override
+    {
+        RELEASE_ASSERT(m_structVariable);
+
+        Base::visit(variableDeclarationsStatement);
+
+        AST::Statements statements;
+        for (UniqueRef<AST::VariableDeclaration>& variableDeclaration : variableDeclarationsStatement.variableDeclarations()) {
+            AST::VariableDeclaration& variable = variableDeclaration.get();
+
+            {
+                AST::VariableDeclarations declarations;
+                declarations.append(WTFMove(variableDeclaration));
+                statements.append(makeUniqueRef<AST::VariableDeclarationsStatement>(variable.origin(), WTFMove(declarations)));
+            }
+
+            auto iter = m_variableMapping.find(&variable);
+            if (iter != m_variableMapping.end())
+                statements.append(makeUniqueRef<AST::EffectfulExpressionStatement>(assignVariableIntoStruct(variable, iter->value)));
+        }
+
+        auto origin = Lexer::Token(variableDeclarationsStatement.origin());
+        AST::replaceWith<AST::StatementList>(variableDeclarationsStatement, WTFMove(origin), WTFMove(statements));
+    }
+
+private:
+    AST::VariableDeclaration* m_structVariable { nullptr };
+
+    UniqueRef<AST::TypeReference>& m_structType;
+    UniqueRef<AST::PointerType> m_pointerToStructType;
+    // If this mapping contains the variable, it means that the variable's canonical location
+    // is in the struct we use to preserve variable lifetimes.
+    const HashMap<AST::VariableDeclaration*, AST::StructureElement*>& m_variableMapping;
+};
+
+void preserveVariableLifetimes(Program& program)
+{
+    HashMap<AST::VariableDeclaration*, String> escapedVariables;
+    {
+        EscapedVariableCollector collector;
+        collector.Visitor::visit(program);
+        escapedVariables = collector.takeEscapedVariables();
+    }
+
+    AST::StructureElements elements;
+    for (auto& pair : escapedVariables) {
+        auto* variable = pair.key;
+        String name = pair.value;
+        elements.append(AST::StructureElement { Lexer::Token(variable->origin()), { }, variable->type()->clone(), WTFMove(name), WTF::nullopt });
+    }
+
+    // Name of this doesn't matter, since we don't use struct names when
+    // generating Metal type names. We just pick something here to make it
+    // easy to read in AST dumps.
+    auto wrapperStructDefinition = makeUniqueRef<AST::StructureDefinition>(anonymousToken(Lexer::Token::Type::Struct), "__WrapperStruct__"_s, WTFMove(elements));
+
+    HashMap<AST::VariableDeclaration*, AST::StructureElement*> variableMapping;
+    unsigned index = 0;
+    for (auto& pair : escapedVariables)
+        variableMapping.add(pair.key, &wrapperStructDefinition->structureElements()[index++]);
+
+    {
+        auto wrapperStructType = AST::TypeReference::wrap(anonymousToken(Lexer::Token::Type::Identifier), wrapperStructDefinition);
+        PreserveLifetimes preserveLifetimes(wrapperStructType, variableMapping);
+        preserveLifetimes.Visitor::visit(program);
+    }
+
+    program.structureDefinitions().append(WTFMove(wrapperStructDefinition));
+}
+
+} // namespace WHLSL
+
+} // namespace WebCore
+
+#endif // ENABLE(WEBGPU)
diff --git a/Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h b/Source/WebCore/Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.h
new file mode 100644 (file)
index 0000000..d5d9288
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBGPU)
+
+namespace WebCore {
+
+namespace WHLSL {
+
+class Program;
+
+void preserveVariableLifetimes(Program&);
+
+}
+
+}
+
+#endif
index 9277f84..d45c5b2 100644 (file)
@@ -300,6 +300,12 @@ void Visitor::visit(AST::Block& block)
         checkErrorAndVisit(statement);
 }
 
+void Visitor::visit(AST::StatementList& statementList)
+{
+    for (auto& statement : statementList.statements())
+        checkErrorAndVisit(statement);
+}
+
 void Visitor::visit(AST::Statement& statement)
 {
     if (is<AST::Block>(statement))
@@ -320,6 +326,8 @@ void Visitor::visit(AST::Statement& statement)
         checkErrorAndVisit(downcast<AST::IfStatement>(statement));
     else if (is<AST::Return>(statement))
         checkErrorAndVisit(downcast<AST::Return>(statement));
+    else if (is<AST::StatementList>(statement))
+        checkErrorAndVisit(downcast<AST::StatementList>(statement));
     else if (is<AST::SwitchCase>(statement))
         checkErrorAndVisit(downcast<AST::SwitchCase>(statement));
     else if (is<AST::SwitchStatement>(statement))
@@ -376,6 +384,8 @@ void Visitor::visit(AST::Expression& expression)
         checkErrorAndVisit(downcast<AST::NullLiteral>(expression));
     else if (is<AST::DotExpression>(expression))
         checkErrorAndVisit(downcast<AST::DotExpression>(expression));
+    else if (is<AST::GlobalVariableReference>(expression))
+        checkErrorAndVisit(downcast<AST::GlobalVariableReference>(expression));
     else if (is<AST::IndexExpression>(expression))
         checkErrorAndVisit(downcast<AST::IndexExpression>(expression));
     else if (is<AST::ReadModifyWriteExpression>(expression))
@@ -397,6 +407,11 @@ void Visitor::visit(AST::DotExpression& dotExpression)
     checkErrorAndVisit(static_cast<AST::PropertyAccessExpression&>(dotExpression));
 }
 
+void Visitor::visit(AST::GlobalVariableReference& globalVariableReference)
+{
+    checkErrorAndVisit(globalVariableReference.base());
+}
+
 void Visitor::visit(AST::IndexExpression& indexExpression)
 {
     checkErrorAndVisit(indexExpression.indexExpression());
index 04dbc6b..ec396fd 100644 (file)
@@ -66,12 +66,14 @@ class BooleanLiteral;
 class EnumerationMemberLiteral;
 class NumThreadsFunctionAttribute;
 class Block;
+class StatementList;
 class Statement;
 class Break;
 class Continue;
 class DoWhileLoop;
 class Expression;
 class DotExpression;
+class GlobalVariableReference;
 class IndexExpression;
 class PropertyAccessExpression;
 class EffectfulExpressionStatement;
@@ -145,12 +147,14 @@ public:
     virtual void visit(AST::FunctionAttribute&);
     virtual void visit(AST::NumThreadsFunctionAttribute&);
     virtual void visit(AST::Block&);
+    virtual void visit(AST::StatementList&);
     virtual void visit(AST::Statement&);
     virtual void visit(AST::Break&);
     virtual void visit(AST::Continue&);
     virtual void visit(AST::DoWhileLoop&);
     virtual void visit(AST::Expression&);
     virtual void visit(AST::DotExpression&);
+    virtual void visit(AST::GlobalVariableReference&);
     virtual void visit(AST::IndexExpression&);
     virtual void visit(AST::PropertyAccessExpression&);
     virtual void visit(AST::EffectfulExpressionStatement&);
index 4024236..27ed8b8 100644 (file)
@@ -322,6 +322,7 @@ Modules/webgpu/WHLSL/WHLSLIntrinsics.cpp
 Modules/webgpu/WHLSL/WHLSLStatementBehaviorChecker.cpp
 Modules/webgpu/WHLSL/WHLSLNameContext.cpp
 Modules/webgpu/WHLSL/WHLSLNameResolver.cpp
+Modules/webgpu/WHLSL/WHLSLPreserveVariableLifetimes.cpp
 Modules/webgpu/WHLSL/WHLSLResolveOverloadImpl.cpp
 Modules/webgpu/WHLSL/WHLSLRecursionChecker.cpp
 Modules/webgpu/WHLSL/WHLSLVisitor.cpp
index 52db78f..3c91f43 100644 (file)
                51FB67DA1AE6B5E400D06C5A /* ContentExtensionStyleSheet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionStyleSheet.h; sourceTree = "<group>"; };
                52131E5A1C4F15610033F802 /* VideoFullscreenInterfaceMac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = VideoFullscreenInterfaceMac.mm; sourceTree = "<group>"; };
                5215862C229377B7005925EF /* WHLSLAST.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLAST.h; sourceTree = "<group>"; };
+               522DA3D3229E1D390042D151 /* WHLSLGlobalVariableReference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLGlobalVariableReference.h; sourceTree = "<group>"; };
+               522E1A172297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WHLSLPreserveVariableLifetimes.cpp; sourceTree = "<group>"; };
+               522E1A192297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLPreserveVariableLifetimes.h; sourceTree = "<group>"; };
                526724F11CB2FDF60075974D /* TextTrackRepresentationCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TextTrackRepresentationCocoa.mm; sourceTree = "<group>"; };
                526724F21CB2FDF60075974D /* TextTrackRepresentationCocoa.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextTrackRepresentationCocoa.h; sourceTree = "<group>"; };
                52B0D4BD1C57FD1E0077CE53 /* PlatformView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformView.h; sourceTree = "<group>"; };
                                C21BF70521CD89C700227979 /* WHLSLFunctionAttribute.h */,
                                C21BF6FD21CD89C000227979 /* WHLSLFunctionDeclaration.h */,
                                C21BF6F421CD89B300227979 /* WHLSLFunctionDefinition.h */,
+                               522DA3D3229E1D390042D151 /* WHLSLGlobalVariableReference.h */,
                                C21BF6FF21CD89C200227979 /* WHLSLIfStatement.h */,
                                C21BF6F721CD89B900227979 /* WHLSLIndexExpression.h */,
                                C20F4F6621DFF2360070C45A /* WHLSLIntegerLiteral.cpp */,
                                C24A57AF21FAD53F004C6DD1 /* WHLSLPipelineDescriptor.h */,
                                C24A57BA21FEAFEA004C6DD1 /* WHLSLPrepare.cpp */,
                                C24A57BB21FEAFEA004C6DD1 /* WHLSLPrepare.h */,
+                               522E1A172297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.cpp */,
+                               522E1A192297D6D400E5D36A /* WHLSLPreserveVariableLifetimes.h */,
                                C21BF73A21CD8D7000227979 /* WHLSLProgram.h */,
                                1CAA82F62242AE0500E84BBB /* WHLSLPropertyResolver.cpp */,
                                1CAA82F72242AE0500E84BBB /* WHLSLPropertyResolver.h */,