[spirv] Use object of correct base class for method call (#3671)

When calling a method using an object, we have to correctly pass "this"
object. When the class of the object used for the method call has
multiple base classes, it does not use the correct base class. It simply
uses "this" object. This commit fixes the issue.

Fixes #3644
This commit is contained in:
Jaebaek Seo 2021-04-12 13:37:13 -04:00 коммит произвёл GitHub
Родитель d31f05ffd6
Коммит c9249f313c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
5 изменённых файлов: 126 добавлений и 0 удалений

Просмотреть файл

@ -2193,6 +2193,36 @@ SpirvInstruction *SpirvEmitter::doCallExpr(const CallExpr *callExpr) {
return processCall(callExpr); return processCall(callExpr);
} }
SpirvInstruction *SpirvEmitter::getBaseOfMemberFunction(QualType objectType,
SpirvInstruction * objInstr,
const CXXMethodDecl* memberFn,
SourceLocation loc) {
// If objectType is different from the parent of memberFn, memberFn should be
// defined in a base struct/class of objectType. We create OpAccessChain with
// index 0 while iterating bases of objectType until we find the base with
// the definition of memberFn.
if (const auto *ptrType = objectType->getAs<PointerType>()) {
if (const auto *recordType = ptrType->getPointeeType()->getAs<RecordType>()) {
const auto *parentDeclOfMemberFn = memberFn->getParent();
if (recordType->getDecl() != parentDeclOfMemberFn) {
const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
auto *zero =
spvBuilder.getConstantInt(astContext.UnsignedIntTy, llvm::APInt(32, 0));
for (auto baseItr = cxxRecordDecl->bases_begin(), itrEnd = cxxRecordDecl->bases_end();
baseItr != itrEnd; baseItr++) {
const auto *baseType = baseItr->getType()->getAs<RecordType>();
objectType = astContext.getPointerType(baseType->desugar());
objInstr = spvBuilder.createAccessChain(objectType,
objInstr, {zero},
loc);
if (baseType->getDecl() == parentDeclOfMemberFn) return objInstr;
}
}
}
}
return nullptr;
}
SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) { SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) {
const FunctionDecl *callee = getCalleeDefinition(callExpr); const FunctionDecl *callee = getCalleeDefinition(callExpr);
@ -2243,6 +2273,10 @@ SpirvInstruction *SpirvEmitter::processCall(const CallExpr *callExpr) {
objectType = object->getType(); objectType = object->getType();
objInstr = doExpr(object); objInstr = doExpr(object);
if (auto *accessToBaseInstr = getBaseOfMemberFunction(objectType, objInstr, memberFn, memberCall->getExprLoc())) {
objInstr = accessToBaseInstr;
objectType = accessToBaseInstr->getAstResultType();
}
// If not already a variable, we need to create a temporary variable and // If not already a variable, we need to create a temporary variable and
// pass the object pointer to the function. Example: // pass the object pointer to the function. Example:

Просмотреть файл

@ -975,6 +975,15 @@ private:
SpirvInstruction *sampleIndex, SpirvInstruction *sampleIndex,
SourceLocation loc); SourceLocation loc);
/// \brief Returns OpAccessChain to the struct/class object that defines
/// memberFn when the struct/class is a base struct/class of objectType.
/// If the struct/class that defines memberFn is not a base of objectType,
/// returns nullptr.
SpirvInstruction *getBaseOfMemberFunction(QualType objectType,
SpirvInstruction * objInstr,
const CXXMethodDecl* memberFn,
SourceLocation loc);
private: private:
/// \brief Takes a vector of size 4, and returns a vector of size 1 or 2 or 3 /// \brief Takes a vector of size 4, and returns a vector of size 1 or 2 or 3
/// or 4. Creates a CompositeExtract or VectorShuffle instruction to extract /// or 4. Creates a CompositeExtract or VectorShuffle instruction to extract

Просмотреть файл

@ -0,0 +1,38 @@
// Run: %dxc -T vs_5_0 -E main -fspv-target-env=vulkan1.1
// CHECK: %bar = OpTypeStruct %empty %mat4v4float
// CHECK: %foo = OpTypeStruct %bar
// CHECK: OpFunctionCall %v4float %foo_get %x
struct empty {
};
struct bar : empty {
float4x4 trans;
float4 value() {
return 0;
}
};
struct foo : bar {
float4 value() {
return 1;
}
// When foo calls foo::value(), it must use
// this->value() instead of (this's 0th object)->value()
// CHECK: %foo_get = OpFunction
// CHECK: %param_this = OpFunctionParameter %_ptr_Function_foo
// CHECK: OpFunctionCall %v4float %foo_value %param_this
float4 get() {
return value();
}
};
void main(out float4 Position : SV_Position) {
foo x;
Position = x.get();
}

Просмотреть файл

@ -0,0 +1,38 @@
// Run: %dxc -T vs_5_0 -E main -fspv-target-env=vulkan1.1
// CHECK: %bar = OpTypeStruct %empty %mat4v4float
// CHECK: %foo = OpTypeStruct %bar
// CHECK: OpFunctionCall %v4float %foo_get %x
struct empty {
};
struct bar : empty {
float4x4 trans;
float4 value() {
return mul(float4(1,1,1,0), trans);
}
};
struct foo : bar {
// When foo calls bar::value(), it must use
// (this's 0th object)->value() instead of this->value()
// because this's 0th object is the object of the base struct in SPIR-V.
// CHECK: %foo_get = OpFunction
// CHECK: %param_this = OpFunctionParameter %_ptr_Function_foo
// CHECK: [[bar:%\w+]] = OpAccessChain %_ptr_Function_bar %param_this %uint_0
// CHECK: OpFunctionCall %v4float %bar_value [[bar]]
float4 get() {
return value();
}
};
void main(out float4 Position : SV_Position)
{
foo x;
Position = x.get();
}

Просмотреть файл

@ -652,6 +652,13 @@ TEST_F(FileTest, InheritanceLayoutDifferences) {
TEST_F(FileTest, InheritanceLayoutEmptyStruct) { TEST_F(FileTest, InheritanceLayoutEmptyStruct) {
runFileTest("oo.inheritance.layout.empty-struct.hlsl"); runFileTest("oo.inheritance.layout.empty-struct.hlsl");
} }
TEST_F(FileTest, InheritanceCallMethodOfBase) {
runFileTest("oo.inheritance.call.base.method.hlsl", Expect::Success,
/* runValidation */ false);
}
TEST_F(FileTest, InheritanceCallMethodWithSameBaseMethodName) {
runFileTest("oo.call.method.with.same.base.method.name.hlsl");
}
// For semantics // For semantics
// SV_Position, SV_ClipDistance, and SV_CullDistance are covered in // SV_Position, SV_ClipDistance, and SV_CullDistance are covered in