intrinsics.def: Support [[stage()]] decorations on overloads

Specifies that the overload can only be used in the specific stages.
Actually validating this with the IntrinsicTable is TODO.

Bug: tint:657
Bug: tint:832
Change-Id: I11ffefee22e5f26103f008b23d16066a2a3ba90d
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/53050
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
Reviewed-by: David Neto <dneto@google.com>
diff --git a/src/intrinsics.def b/src/intrinsics.def
index 40a9d88..d63d72a 100644
--- a/src/intrinsics.def
+++ b/src/intrinsics.def
@@ -281,18 +281,18 @@
 fn distance(f32, f32) -> f32
 fn distance<N: num>(vec<N, f32>, vec<N, f32>) -> f32
 fn dot<N: num>(vec<N, f32>, vec<N, f32>) -> f32
-fn dpdx(f32) -> f32
-fn dpdx<N: num>(vec<N, f32>) -> vec<N, f32>
-fn dpdxCoarse(f32) -> f32
-fn dpdxCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
-fn dpdxFine(f32) -> f32
-fn dpdxFine<N: num>(vec<N, f32>) -> vec<N, f32>
-fn dpdy(f32) -> f32
-fn dpdy<N: num>(vec<N, f32>) -> vec<N, f32>
-fn dpdyCoarse(f32) -> f32
-fn dpdyCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
-fn dpdyFine(f32) -> f32
-fn dpdyFine<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdx(f32) -> f32
+[[stage("fragment")]] fn dpdx<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdxCoarse(f32) -> f32
+[[stage("fragment")]] fn dpdxCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdxFine(f32) -> f32
+[[stage("fragment")]] fn dpdxFine<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdy(f32) -> f32
+[[stage("fragment")]] fn dpdy<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdyCoarse(f32) -> f32
+[[stage("fragment")]] fn dpdyCoarse<N: num>(vec<N, f32>) -> vec<N, f32>
+[[stage("fragment")]] fn dpdyFine(f32) -> f32
+[[stage("fragment")]] fn dpdyFine<N: num>(vec<N, f32>) -> vec<N, f32>
 fn exp(f32) -> f32
 fn exp<N: num>(vec<N, f32>) -> vec<N, f32>
 fn exp2(f32) -> f32
@@ -367,7 +367,7 @@
 fn sqrt<N: num>(vec<N, f32>) -> vec<N, f32>
 fn step(f32, f32) -> f32
 fn step<N: num>(vec<N, f32>, vec<N, f32>) -> vec<N, f32>
-fn storageBarrier()
+[[stage("compute")]] fn storageBarrier()
 fn tan(f32) -> f32
 fn tan<N: num>(vec<N, f32>) -> vec<N, f32>
 fn tanh(f32) -> f32
@@ -379,7 +379,7 @@
 fn unpack2x16unorm(u32) -> vec2<f32>
 fn unpack4x8snorm(u32) -> vec4<f32>
 fn unpack4x8unorm(u32) -> vec4<f32>
-fn workgroupBarrier()
+[[stage("compute")]] fn workgroupBarrier()
 fn textureDimensions<T: fiu32>(texture: texture_1d<T>) -> i32
 fn textureDimensions<T: fiu32>(texture: texture_2d<T>) -> vec2<i32>
 fn textureDimensions<T: fiu32>(texture: texture_2d<T>, level: i32) -> vec2<i32>
@@ -420,36 +420,36 @@
 fn textureNumLevels(texture: texture_depth_cube) -> i32
 fn textureNumLevels(texture: texture_depth_cube_array) -> i32
 fn textureNumSamples<T: fiu32>(texture: texture_multisampled_2d<T>) -> i32
-fn textureSample(texture: texture_1d<f32>, sampler: sampler, coords: f32) -> vec4<f32>
-fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
-fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
-fn textureSample(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32) -> vec4<f32>
-fn textureSample(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> vec4<f32>
-fn textureSample(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>) -> vec4<f32>
-fn textureSample(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, offset: vec3<i32>) -> vec4<f32>
-fn textureSample(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>) -> vec4<f32>
-fn textureSample(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32) -> vec4<f32>
-fn textureSample(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>) -> f32
-fn textureSample(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> f32
-fn textureSample(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32) -> f32
-fn textureSample(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> f32
-fn textureSample(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> f32
-fn textureSample(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32) -> f32
-fn textureSample(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
-fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32>
-fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32, offset: vec2<i32>) -> vec4<f32>
-fn textureSampleBias(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, bias: f32) -> vec4<f32>
-fn textureSampleBias(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, bias: f32, offset: vec2<i32>) -> vec4<f32>
-fn textureSampleBias(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
-fn textureSampleBias(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, bias: f32, offset: vec3<i32>) -> vec4<f32>
-fn textureSampleBias(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
-fn textureSampleBias(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32, bias: f32) -> vec4<f32>
-fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32
-fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32
-fn textureSampleCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> f32
-fn textureSampleCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32, offset: vec2<i32>) -> f32
-fn textureSampleCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32
-fn textureSampleCompare(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_1d<f32>, sampler: sampler, coords: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, offset: vec3<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32) -> vec4<f32>
+[[stage("fragment")]] fn textureSample(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_depth_2d, sampler: sampler, coords: vec2<f32>, offset: vec2<i32>) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_depth_2d_array, sampler: sampler, coords: vec2<f32>, array_index: i32, offset: vec2<i32>) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_depth_cube, sampler: sampler, coords: vec3<f32>) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_depth_cube_array, sampler: sampler, coords: vec3<f32>, array_index: i32) -> f32
+[[stage("fragment")]] fn textureSample(texture: texture_external, sampler: sampler, coords: vec2<f32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, bias: f32, offset: vec2<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, bias: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, bias: f32, offset: vec2<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_3d<f32>, sampler: sampler, coords: vec3<f32>, bias: f32, offset: vec3<i32>) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_cube<f32>, sampler: sampler, coords: vec3<f32>, bias: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleBias(texture: texture_cube_array<f32>, sampler: sampler, coords: vec3<f32>, array_index: i32, bias: f32) -> vec4<f32>
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> f32
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_2d, sampler: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> f32
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32) -> f32
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_2d_array, sampler: sampler_comparison, coords: vec2<f32>, array_index: i32, depth_ref: f32, offset: vec2<i32>) -> f32
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_cube, sampler: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> f32
+[[stage("fragment")]] fn textureSampleCompare(texture: texture_depth_cube_array, sampler: sampler_comparison, coords: vec3<f32>, array_index: i32, depth_ref: f32) -> f32
 fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32>
 fn textureSampleGrad(texture: texture_2d<f32>, sampler: sampler, coords: vec2<f32>, ddx: vec2<f32>, ddy: vec2<f32>, offset: vec2<i32>) -> vec4<f32>
 fn textureSampleGrad(texture: texture_2d_array<f32>, sampler: sampler, coords: vec2<f32>, array_index: i32, ddx: vec2<f32>, ddy: vec2<f32>) -> vec4<f32>
diff --git a/tools/src/cmd/intrinsic-gen/ast/ast.go b/tools/src/cmd/intrinsic-gen/ast/ast.go
index e7000f3..8d1da0e 100644
--- a/tools/src/cmd/intrinsic-gen/ast/ast.go
+++ b/tools/src/cmd/intrinsic-gen/ast/ast.go
@@ -102,6 +102,7 @@
 type FunctionDecl struct {
 	Source         tok.Source
 	Name           string
+	Decorations    Decorations
 	TemplateParams TemplateParams
 	Parameters     Parameters
 	ReturnType     *TemplatedName
diff --git a/tools/src/cmd/intrinsic-gen/parser/parser.go b/tools/src/cmd/intrinsic-gen/parser/parser.go
index 8434305..4ab3516 100644
--- a/tools/src/cmd/intrinsic-gen/parser/parser.go
+++ b/tools/src/cmd/intrinsic-gen/parser/parser.go
@@ -43,20 +43,31 @@
 
 func (p *parser) parse() (*ast.AST, error) {
 	out := ast.AST{}
+	var decorations ast.Decorations
 	for p.err == nil {
 		t := p.peek(0)
 		if t == nil {
 			break
 		}
 		switch t.Kind {
+		case tok.Ldeco:
+			decorations = append(decorations, p.decorations()...)
 		case tok.Enum:
+			if len(decorations) > 0 {
+				p.err = fmt.Errorf("%v unexpected decoration", decorations[0].Source)
+			}
 			out.Enums = append(out.Enums, p.enumDecl())
 		case tok.Match:
+			if len(decorations) > 0 {
+				p.err = fmt.Errorf("%v unexpected decoration", decorations[0].Source)
+			}
 			out.Matchers = append(out.Matchers, p.matcherDecl())
-		case tok.Type, tok.Ldeco:
-			out.Types = append(out.Types, p.typeDecl())
+		case tok.Type:
+			out.Types = append(out.Types, p.typeDecl(decorations))
+			decorations = nil
 		case tok.Function:
-			out.Functions = append(out.Functions, p.functionDecl())
+			out.Functions = append(out.Functions, p.functionDecl(decorations))
+			decorations = nil
 		default:
 			p.err = fmt.Errorf("%v unexpected token '%v'", t.Source, t.Kind)
 		}
@@ -98,8 +109,7 @@
 	return m
 }
 
-func (p *parser) typeDecl() ast.TypeDecl {
-	decos := p.decorations()
+func (p *parser) typeDecl(decos ast.Decorations) ast.TypeDecl {
 	p.expect(tok.Type, "type declaration")
 	name := p.expect(tok.Identifier, "type name")
 	m := ast.TypeDecl{
@@ -143,10 +153,14 @@
 	return out
 }
 
-func (p *parser) functionDecl() ast.FunctionDecl {
+func (p *parser) functionDecl(decos ast.Decorations) ast.FunctionDecl {
 	p.expect(tok.Function, "function declaration")
 	name := p.expect(tok.Identifier, "function name")
-	f := ast.FunctionDecl{Source: name.Source, Name: string(name.Runes)}
+	f := ast.FunctionDecl{
+		Source:      name.Source,
+		Decorations: decos,
+		Name:        string(name.Runes),
+	}
 	if p.peekIs(0, tok.Lt) {
 		f.TemplateParams = p.templateParams()
 	}
diff --git a/tools/src/cmd/intrinsic-gen/parser/parser_test.go b/tools/src/cmd/intrinsic-gen/parser/parser_test.go
index 774dc0d..fdc884b 100644
--- a/tools/src/cmd/intrinsic-gen/parser/parser_test.go
+++ b/tools/src/cmd/intrinsic-gen/parser/parser_test.go
@@ -95,6 +95,14 @@
 				Name: "F",
 			}},
 		}},
+		{"[[deco]] fn F()", ast.AST{
+			Functions: []ast.FunctionDecl{{
+				Name: "F",
+				Decorations: ast.Decorations{
+					{Name: "deco"},
+				},
+			}},
+		}},
 		{"fn F(a)", ast.AST{
 			Functions: []ast.FunctionDecl{{
 				Name: "F",
diff --git a/tools/src/cmd/intrinsic-gen/resolver/resolve.go b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
index 0f1a1e9..4f76f31 100644
--- a/tools/src/cmd/intrinsic-gen/resolver/resolve.go
+++ b/tools/src/cmd/intrinsic-gen/resolver/resolve.go
@@ -243,13 +243,43 @@
 		return err
 	}
 
-	// Construct the semantic overload and append it to the function
+	// Construct the semantic overload
 	overload := &sem.Overload{
 		Decl:           a,
 		Function:       f,
 		Parameters:     make([]sem.Parameter, len(a.Parameters)),
 		TemplateParams: templateParams,
 	}
+
+	// Process overload decorations
+	if stageDeco := a.Decorations.Take("stage"); stageDeco != nil {
+		for stageDeco != nil {
+			for _, stage := range stageDeco.Values {
+				switch stage {
+				case "vertex":
+					overload.CanBeUsedInStage.Vertex = true
+				case "fragment":
+					overload.CanBeUsedInStage.Fragment = true
+				case "compute":
+					overload.CanBeUsedInStage.Compute = true
+				default:
+					return fmt.Errorf("%v unknown stage '%v'", stageDeco.Source, stage)
+				}
+			}
+			stageDeco = a.Decorations.Take("stage")
+		}
+	} else {
+		overload.CanBeUsedInStage = sem.StageUses{
+			Vertex:   true,
+			Fragment: true,
+			Compute:  true,
+		}
+	}
+	if len(a.Decorations) != 0 {
+		return fmt.Errorf("%v unknown decoration", a.Decorations[0].Source)
+	}
+
+	// Append the overload to the function
 	f.Overloads = append(f.Overloads, overload)
 
 	// Sort the template parameters by resolved type. Append these to
diff --git a/tools/src/cmd/intrinsic-gen/sem/sem.go b/tools/src/cmd/intrinsic-gen/sem/sem.go
index b7b49c5..36548ae 100644
--- a/tools/src/cmd/intrinsic-gen/sem/sem.go
+++ b/tools/src/cmd/intrinsic-gen/sem/sem.go
@@ -129,13 +129,21 @@
 
 // Overload describes a single overload of a function
 type Overload struct {
-	Decl           ast.FunctionDecl
-	Function       *Function
-	TemplateParams []TemplateParam
-	OpenTypes      []*TemplateTypeParam
-	OpenNumbers    []TemplateParam
-	ReturnType     *FullyQualifiedName
-	Parameters     []Parameter
+	Decl             ast.FunctionDecl
+	Function         *Function
+	TemplateParams   []TemplateParam
+	OpenTypes        []*TemplateTypeParam
+	OpenNumbers      []TemplateParam
+	ReturnType       *FullyQualifiedName
+	Parameters       []Parameter
+	CanBeUsedInStage StageUses
+}
+
+// StageUses describes the stages an overload can be used in
+type StageUses struct {
+	Vertex   bool
+	Fragment bool
+	Compute  bool
 }
 
 // Format implements the fmt.Formatter interface