--- /dev/null
+//===- ModuleCombiner.cpp - MLIR SPIR-V Module Combiner ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the the SPIR-V module combiner library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/SPIRV/ModuleCombiner.h"
+
+#include "mlir/Dialect/SPIRV/SPIRVOps.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/SymbolTable.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringExtras.h"
+
+using namespace mlir;
+
+static constexpr unsigned maxFreeID = 1 << 20;
+
+static SmallString<64> renameSymbol(StringRef oldSymName, unsigned &lastUsedID,
+ spirv::ModuleOp combinedModule) {
+ SmallString<64> newSymName(oldSymName);
+ newSymName.push_back('_');
+
+ while (lastUsedID < maxFreeID) {
+ std::string possible = (newSymName + llvm::utostr(++lastUsedID)).str();
+
+ if (!SymbolTable::lookupSymbolIn(combinedModule, possible)) {
+ newSymName += llvm::utostr(lastUsedID);
+ break;
+ }
+ }
+
+ return newSymName;
+}
+
+/// Check if a symbol with the same name as op already exists in source. If so,
+/// rename op and update all its references in target.
+static LogicalResult updateSymbolAndAllUses(SymbolOpInterface op,
+ spirv::ModuleOp target,
+ spirv::ModuleOp source,
+ unsigned &lastUsedID) {
+ if (!SymbolTable::lookupSymbolIn(source, op.getName()))
+ return success();
+
+ StringRef oldSymName = op.getName();
+ SmallString<64> newSymName = renameSymbol(oldSymName, lastUsedID, target);
+
+ if (failed(SymbolTable::replaceAllSymbolUses(op, newSymName, target)))
+ return op.emitError("unable to update all symbol uses for ")
+ << oldSymName << " to " << newSymName;
+
+ SymbolTable::setSymbolName(op, newSymName);
+ return success();
+}
+
+namespace mlir {
+namespace spirv {
+
+// TODO Properly test symbol rename listener mechanism.
+
+OwningSPIRVModuleRef
+combine(llvm::MutableArrayRef<spirv::ModuleOp> modules,
+ OpBuilder &combinedModuleBuilder,
+ llvm::function_ref<void(ModuleOp, StringRef, StringRef)>
+ symRenameListener) {
+ unsigned lastUsedID = 0;
+
+ if (modules.empty())
+ return nullptr;
+
+ auto addressingModel = modules[0].addressing_model();
+ auto memoryModel = modules[0].memory_model();
+
+ auto combinedModule = combinedModuleBuilder.create<spirv::ModuleOp>(
+ modules[0].getLoc(), addressingModel, memoryModel);
+ combinedModuleBuilder.setInsertionPointToStart(&*combinedModule.getBody());
+
+ // In some cases, a symbol in the (current state of the) combined module is
+ // renamed in order to maintain the conflicting symbol in the input module
+ // being merged. For example, if the conflict is between a global variable in
+ // the current combined module and a function in the input module, the global
+ // varaible is renamed. In order to notify listeners of the symbol updates in
+ // such cases, we need to keep track of the module from which the renamed
+ // symbol in the combined module originated. This map keeps such information.
+ DenseMap<StringRef, spirv::ModuleOp> symNameToModuleMap;
+
+ for (auto module : modules) {
+ if (module.addressing_model() != addressingModel ||
+ module.memory_model() != memoryModel) {
+ module.emitError(
+ "input modules differ in addressing model and/or memory model");
+ return nullptr;
+ }
+
+ spirv::ModuleOp moduleClone = module.clone();
+
+ // In the combined module, rename all symbols that conflict with symbols
+ // from the current input module. This renmaing applies to all ops except
+ // for spv.funcs. This way, if the conflicting op in the input module is
+ // non-spv.func, we rename that symbol instead and maintain the spv.func in
+ // the combined module name as it is.
+ for (auto &op : combinedModule.getBlock().without_terminator()) {
+ if (auto symbolOp = dyn_cast<SymbolOpInterface>(op)) {
+ StringRef oldSymName = symbolOp.getName();
+
+ if (!isa<FuncOp>(op) &&
+ failed(updateSymbolAndAllUses(symbolOp, combinedModule, moduleClone,
+ lastUsedID)))
+ return nullptr;
+
+ StringRef newSymName = symbolOp.getName();
+
+ if (symRenameListener && oldSymName != newSymName) {
+ spirv::ModuleOp originalModule =
+ symNameToModuleMap.lookup(oldSymName);
+
+ if (!originalModule) {
+ module.emitError("unable to find original ModuleOp for symbol ")
+ << oldSymName;
+ return nullptr;
+ }
+
+ symRenameListener(originalModule, oldSymName, newSymName);
+
+ // Since the symbol name is updated, there is no need to maintain the
+ // entry that assocaites the old symbol name with the original module.
+ symNameToModuleMap.erase(oldSymName);
+ // Instead, add a new entry to map the new symbol name to the original
+ // module in case it gets renamed again later.
+ symNameToModuleMap[newSymName] = originalModule;
+ }
+ }
+ }
+
+ // In the current input module, rename all symbols that conflict with
+ // symbols from the combined module. This includes renaming spv.funcs.
+ for (auto &op : moduleClone.getBlock().without_terminator()) {
+ if (auto symbolOp = dyn_cast<SymbolOpInterface>(op)) {
+ StringRef oldSymName = symbolOp.getName();
+
+ if (failed(updateSymbolAndAllUses(symbolOp, moduleClone, combinedModule,
+ lastUsedID)))
+ return nullptr;
+
+ StringRef newSymName = symbolOp.getName();
+
+ if (symRenameListener && oldSymName != newSymName) {
+ symRenameListener(module, oldSymName, newSymName);
+
+ // Insert the module associated with the symbol name.
+ auto emplaceResult =
+ symNameToModuleMap.try_emplace(symbolOp.getName(), module);
+
+ // If an entry with the same symbol name is already present, this must
+ // be a problem with the implementation, specially clean-up of the map
+ // while iterating over the combined module above.
+ if (!emplaceResult.second) {
+ module.emitError("did not expect to find an entry for symbol ")
+ << symbolOp.getName();
+ return nullptr;
+ }
+ }
+ }
+ }
+
+ // Clone all the module's ops to the combined module.
+ for (auto &op : moduleClone.getBlock().without_terminator())
+ combinedModuleBuilder.insert(op.clone());
+ }
+
+ return combinedModule;
+}
+
+} // namespace spirv
+} // namespace mlir
--- /dev/null
+// RUN: mlir-opt -test-spirv-module-combiner -split-input-file -verify-diagnostics %s | FileCheck %s
+
+// Test basic renaming of conflicting funcOps.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+}
+}
+
+// -----
+
+// Test basic renaming of conflicting funcOps across 3 modules.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_2
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+}
+
+// -----
+
+// Test properly updating references to a renamed funcOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv.FunctionCall @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+
+ spv.func @bar(%arg0 : f32) -> f32 "None" {
+ %0 = spv.FunctionCall @foo(%arg0) : (f32) -> (f32)
+ spv.ReturnValue %0 : f32
+ }
+}
+}
+
+// -----
+
+// Test properly updating references to a renamed funcOp if the functionCallOp
+// preceeds the callee funcOp definition.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv.FunctionCall @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @bar(%arg0 : f32) -> f32 "None" {
+ %0 = spv.FunctionCall @foo(%arg0) : (f32) -> (f32)
+ spv.ReturnValue %0 : f32
+ }
+
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+}
+}
+
+// -----
+
+// Test properly updating entryPointOp and executionModeOp attached to renamed
+// funcOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.EntryPoint "GLCompute" @foo_1
+// CHECK-NEXT: spv.ExecutionMode @foo_1 "ContractionOff"
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+
+ spv.EntryPoint "GLCompute" @foo
+ spv.ExecutionMode @foo "ContractionOff"
+}
+}
+
+// -----
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.EntryPoint "GLCompute" @fo
+// CHECK-NEXT: spv.ExecutionMode @foo "ContractionOff"
+
+// CHECK-NEXT: spv.func @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.EntryPoint "GLCompute" @foo_1
+// CHECK-NEXT: spv.ExecutionMode @foo_1 "ContractionOff"
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+
+ spv.EntryPoint "GLCompute" @foo
+ spv.ExecutionMode @foo "ContractionOff"
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+
+ spv.EntryPoint "GLCompute" @foo
+ spv.ExecutionMode @foo "ContractionOff"
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and globalVariableOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.globalVariable @foo_1
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and globalVariableOp and update the global variable's
+// references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.globalVariable @foo_1
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv._address_of @foo_1
+// CHECK-NEXT: spv.Load
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+
+ spv.func @bar() -> f32 "None" {
+ %0 = spv._address_of @foo : !spv.ptr<f32, Input>
+ %1 = spv.Load "Input" %0 : f32
+ spv.ReturnValue %1 : f32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting globalVariableOp and funcOp and update the global variable's
+// references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.globalVariable @foo_1
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv._address_of @foo_1
+// CHECK-NEXT: spv.Load
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+
+ spv.func @bar() -> f32 "None" {
+ %0 = spv._address_of @foo : !spv.ptr<f32, Input>
+ %1 = spv.Load "Input" %0 : f32
+ spv.ReturnValue %1 : f32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and specConstantOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.specConstant @foo_1
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @foo = -5 : i32
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and specConstantOp and update the spec constant's
+// references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.specConstant @foo_1
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv._reference_of @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @foo = -5 : i32
+
+ spv.func @bar() -> i32 "None" {
+ %0 = spv._reference_of @foo : i32
+ spv.ReturnValue %0 : i32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting specConstantOp and funcOp and update the spec constant's
+// references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.specConstant @foo_1
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv._reference_of @foo_1
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.specConstant @foo = -5 : i32
+
+ spv.func @bar() -> i32 "None" {
+ %0 = spv._reference_of @foo : i32
+ spv.ReturnValue %0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and specConstantCompositeOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.specConstant @bar
+// CHECK-NEXT: spv.specConstantComposite @foo_1 (@bar, @bar)
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+}
+}
+
+// -----
+
+// Resolve conflicting funcOp and specConstantCompositeOp and update the spec
+// constant's references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.specConstant @bar
+// CHECK-NEXT: spv.specConstantComposite @foo_1 (@bar, @bar)
+// CHECK-NEXT: spv.func @baz
+// CHECK-NEXT: spv._reference_of @foo_1
+// CHECK-NEXT: spv.CompositeExtract
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+
+ spv.func @baz() -> i32 "None" {
+ %0 = spv._reference_of @foo : !spv.array<2 x i32>
+ %1 = spv.CompositeExtract %0[0 : i32] : !spv.array<2 x i32>
+ spv.ReturnValue %1 : i32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting specConstantCompositeOp and funcOp and update the spec
+// constant's references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.specConstant @bar
+// CHECK-NEXT: spv.specConstantComposite @foo_1 (@bar, @bar)
+// CHECK-NEXT: spv.func @baz
+// CHECK-NEXT: spv._reference_of @foo_1
+// CHECK-NEXT: spv.CompositeExtract
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+
+ spv.func @baz() -> i32 "None" {
+ %0 = spv._reference_of @foo : !spv.array<2 x i32>
+ %1 = spv.CompositeExtract %0[0 : i32] : !spv.array<2 x i32>
+ spv.ReturnValue %1 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting spec constants and funcOps and update the spec constant's
+// references.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.specConstant @bar_1
+// CHECK-NEXT: spv.specConstantComposite @foo_2 (@bar_1, @bar_1)
+// CHECK-NEXT: spv.func @baz
+// CHECK-NEXT: spv._reference_of @foo_2
+// CHECK-NEXT: spv.CompositeExtract
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @foo
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+
+// CHECK-NEXT: spv.func @bar
+// CHECK-NEXT: spv.ReturnValue
+// CHECK-NEXT: }
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+
+ spv.func @baz() -> i32 "None" {
+ %0 = spv._reference_of @foo : !spv.array<2 x i32>
+ %1 = spv.CompositeExtract %0[0 : i32] : !spv.array<2 x i32>
+ spv.ReturnValue %1 : i32
+ }
+}
+
+spv.module Logical GLSL450 {
+ spv.func @foo(%arg0 : i32) -> i32 "None" {
+ spv.ReturnValue %arg0 : i32
+ }
+
+ spv.func @bar(%arg0 : f32) -> f32 "None" {
+ spv.ReturnValue %arg0 : f32
+ }
+}
+}
+
+// -----
+
+// Resolve conflicting globalVariableOps.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.globalVariable @foo_1
+
+// CHECK-NEXT: spv.globalVariable @foo
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+}
+
+// -----
+
+// Resolve conflicting globalVariableOp and specConstantOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.globalVariable @foo_1
+
+// CHECK-NEXT: spv.specConstant @foo
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @foo = -5 : i32
+}
+}
+
+// -----
+
+// Resolve conflicting specConstantOp and globalVariableOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.specConstant @foo_1
+
+// CHECK-NEXT: spv.globalVariable @foo
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.specConstant @foo = -5 : i32
+}
+
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+}
+
+// -----
+
+// Resolve conflicting globalVariableOp and specConstantCompositeOp.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.globalVariable @foo_1
+
+// CHECK-NEXT: spv.specConstant @bar
+// CHECK-NEXT: spv.specConstantComposite @foo (@bar, @bar)
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+}
+}
+
+// -----
+
+// Resolve conflicting globalVariableOp and specConstantComposite.
+
+// CHECK: module {
+// CHECK-NEXT: spv.module Logical GLSL450 {
+// CHECK-NEXT: spv.specConstant @bar
+// CHECK-NEXT: spv.specConstantComposite @foo_1 (@bar, @bar)
+
+// CHECK-NEXT: spv.globalVariable @foo
+// CHECK-NEXT: }
+
+module {
+spv.module Logical GLSL450 {
+ spv.specConstant @bar = -5 : i32
+ spv.specConstantComposite @foo (@bar, @bar) : !spv.array<2 x i32>
+}
+
+spv.module Logical GLSL450 {
+ spv.globalVariable @foo bind(1, 0) : !spv.ptr<f32, Input>
+}
+}