Add transform to substitute overrides with const expressions.

This CL adds a SubstituteOverride transform which will convert
an `override` into a `const`. The transform is provided a map of
(string, double) which matches what the WebGPU API accepts as
data for overrides.

Bug: tint:1582
Change-Id: I6e6bf51b98ce4d4746f8de55128666c36735e585
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96760
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
dan sinclair
2022-07-22 16:05:06 +00:00
committed by Dawn LUCI CQ
parent 9ec7893ad4
commit 256f1116b8
156 changed files with 1235 additions and 659 deletions

View File

@@ -0,0 +1,96 @@
// Copyright 2022 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/transform/substitute_override.h"
#include <functional>
#include "src/tint/program_builder.h"
#include "src/tint/sem/variable.h"
TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride);
TINT_INSTANTIATE_TYPEINFO(tint::transform::SubstituteOverride::Config);
namespace tint::transform {
SubstituteOverride::SubstituteOverride() = default;
SubstituteOverride::~SubstituteOverride() = default;
bool SubstituteOverride::ShouldRun(const Program* program, const DataMap&) const {
for (auto* node : program->AST().GlobalVariables()) {
if (node->Is<ast::Override>()) {
return true;
}
}
return false;
}
void SubstituteOverride::Run(CloneContext& ctx, const DataMap& config, DataMap&) const {
const auto* data = config.Get<Config>();
if (!data) {
ctx.dst->Diagnostics().add_error(diag::System::Transform,
"Missing override substitution data");
return;
}
ctx.ReplaceAll([&](const ast::Override* w) -> const ast::Const* {
auto ident = w->Identifier(ctx.src->Symbols());
auto src = ctx.Clone(w->source);
auto sym = ctx.Clone(w->symbol);
auto* ty = ctx.Clone(w->type);
// No replacement provided, just clone the override node as a const.
auto iter = data->map.find(ident);
if (iter == data->map.end()) {
if (!w->constructor) {
ctx.dst->Diagnostics().add_error(
diag::System::Transform,
"Initializer not provided for override, and override not overridden.");
return nullptr;
}
return ctx.dst->Const(src, sym, ty, ctx.Clone(w->constructor));
}
auto value = iter->second;
auto* ctor = Switch(
ctx.src->Sem().Get(w)->Type(),
[&](const sem::Bool*) { return ctx.dst->Expr(!std::equal_to<double>()(value, 0.0)); },
[&](const sem::I32*) { return ctx.dst->Expr(i32(value)); },
[&](const sem::U32*) { return ctx.dst->Expr(u32(value)); },
[&](const sem::F32*) { return ctx.dst->Expr(f32(value)); },
[&](const sem::F16*) { return ctx.dst->Expr(f16(value)); });
if (!ctor) {
ctx.dst->Diagnostics().add_error(diag::System::Transform,
"Failed to create override expression");
return nullptr;
}
return ctx.dst->Const(src, sym, ty, ctor);
});
ctx.Clone();
}
SubstituteOverride::Config::Config() = default;
SubstituteOverride::Config::Config(const Config&) = default;
SubstituteOverride::Config::~Config() = default;
SubstituteOverride::Config& SubstituteOverride::Config::operator=(const Config&) = default;
} // namespace tint::transform

View File

@@ -0,0 +1,89 @@
// Copyright 2022 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
#define SRC_TINT_TRANSFORM_SUBSTITUTE_OVERRIDE_H_
#include <string>
#include <unordered_map>
#include "src/tint/transform/transform.h"
namespace tint::transform {
/// A transform that replaces overrides with the constant values provided.
///
/// # Example
/// ```
/// override width: f32;
/// @id(1) override height: i32 = 4;
/// override depth = 1i;
/// ```
///
/// When transformed with `width` -> 1, `1` -> 22, `depth` -> 42
///
/// ```
/// const width: f32 = 1f;
/// const height: i32 = 22i;
/// const depth = 42i;
/// ```
///
/// @see crbug.com/tint/1582
class SubstituteOverride final : public Castable<SubstituteOverride, Transform> {
public:
/// Configuration options for the transform
struct Config final : public Castable<Config, Data> {
/// Constructor
Config();
/// Copy constructor
Config(const Config&);
/// Destructor
~Config() override;
/// Assignment operator
/// @returns this Config
Config& operator=(const Config&);
/// The map of override name (either the identifier or id) to value.
/// The value is always a double coming into the transform and will be
/// converted to the correct type through and initializer.
std::unordered_map<std::string, double> map;
};
/// Constructor
SubstituteOverride();
/// Destructor
~SubstituteOverride() override;
/// @param program the program to inspect
/// @param data optional extra transform-specific input data
/// @returns true if this transform should be run for the given program
bool ShouldRun(const Program* program, const DataMap& data = {}) const override;
protected:
/// Runs the transform using the CloneContext built for transforming a
/// program. Run() is responsible for calling Clone() on the CloneContext.
/// @param ctx the CloneContext primed with the input program and
/// ProgramBuilder
/// @param inputs optional extra transform-specific input data
/// @param outputs optional extra transform-specific output data
void Run(CloneContext& ctx, const DataMap& inputs, DataMap& outputs) const override;
};
} // namespace tint::transform
#endif // SRC_TINT_TRANSFORM_SUBSTITUTE_OVERRIDE_H_

View File

@@ -0,0 +1,243 @@
// Copyright 2022 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/transform/substitute_override.h"
#include "src/tint/transform/test_helper.h"
namespace tint::transform {
namespace {
using SubstituteOverrideTest = TransformTest;
TEST_F(SubstituteOverrideTest, Error_NoData) {
auto* src = R"(
override width: i32;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = "error: Missing override substitution data";
DataMap data;
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SubstituteOverrideTest, Error_NoOverrideValue) {
auto* src = R"(
override width: i32;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = "error: Initializer not provided for override, and override not overridden.";
SubstituteOverride::Config cfg;
DataMap data;
data.Add<SubstituteOverride::Config>(cfg);
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SubstituteOverrideTest, Module_NoOverrides) {
auto* src = R"(
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = R"(
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
SubstituteOverride::Config cfg;
DataMap data;
data.Add<SubstituteOverride::Config>(cfg);
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SubstituteOverrideTest, Identifier) {
auto* src = R"(
override i_width: i32;
override i_height = 1i;
override f_width: f32;
override f_height = 1.f;
// TODO(crbug.com/tint/1473)
// override h_width: f16;
// override h_height = 1.h;
override b_width: bool;
override b_height = true;
override o_width = 2i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = R"(
const i_width : i32 = 42i;
const i_height = 11i;
const f_width : f32 = 22.299999237f;
const f_height = 12.399999619f;
const b_width : bool = true;
const b_height = false;
const o_width = 2i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
SubstituteOverride::Config cfg;
cfg.map.insert({"i_width", 42.0});
cfg.map.insert({"i_height", 11.0});
cfg.map.insert({"f_width", 22.3});
cfg.map.insert({"f_height", 12.4});
cfg.map.insert({"h_width", 9.4});
cfg.map.insert({"h_height", 3.4});
cfg.map.insert({"b_width", 1.0});
cfg.map.insert({"b_height", 0.0});
DataMap data;
data.Add<SubstituteOverride::Config>(cfg);
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SubstituteOverrideTest, Id) {
auto* src = R"(
enable f16;
@id(0) override i_width: i32;
@id(10) override i_height = 1i;
@id(1) override f_width: f32;
@id(9) override f_height = 1.f;
// TODO(crbug.com/tint/1473)
// @id(2) override h_width: f16;
// @id(8) override h_height = 1.h;
@id(3) override b_width: bool;
@id(7) override b_height = true;
@id(5) override o_width = 2i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = R"(
enable f16;
const i_width : i32 = 42i;
const i_height = 11i;
const f_width : f32 = 22.299999237f;
const f_height = 12.399999619f;
const b_width : bool = true;
const b_height = false;
const o_width = 2i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
SubstituteOverride::Config cfg;
cfg.map.insert({"0", 42.0});
cfg.map.insert({"10", 11.0});
cfg.map.insert({"1", 22.3});
cfg.map.insert({"9", 12.4});
cfg.map.insert({"2", 9.4});
cfg.map.insert({"8", 3.4});
cfg.map.insert({"3", 1.0});
cfg.map.insert({"7", 0.0});
// No effect because an @id is set for o_width
cfg.map.insert({"o_width", 13});
DataMap data;
data.Add<SubstituteOverride::Config>(cfg);
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
TEST_F(SubstituteOverrideTest, Identifier_Expression) {
auto* src = R"(
override i_height = ~2i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
auto* expect = R"(
const i_height = 11i;
@vertex
fn main() -> @builtin(position) vec4<f32> {
return vec4<f32>();
}
)";
SubstituteOverride::Config cfg;
cfg.map.insert({"i_height", 11.0});
DataMap data;
data.Add<SubstituteOverride::Config>(cfg);
auto got = Run<SubstituteOverride>(src, data);
EXPECT_EQ(expect, str(got));
}
} // namespace
} // namespace tint::transform