Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Emit header files for cpp, cuda and metal targets #3938

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

aroidzap
Copy link
Contributor

@aroidzap aroidzap commented Apr 12, 2024

It is quite cumbersome to work without headers in languages like C++ or CUDA.

I tried to add an experimental CodeGenTargets CPPHeader and CUDAHeader.

Everything marked as *ExternCppDecoration is exported in header. In case of functions and structs the ExternCppDecoration is transitively propagated to its function parameters or struct members.


* explicitly prefixed by __extern_cpp in slang, or implicitly generated by [CudaKernel], [DllExport] decorations or others

@csyonghe
Copy link
Collaborator

I think the changes here are fine.

We do need to expose these as new target, CppHeader and CudaHeader.

You can keep the isEmittingHeader option in the emitter, just route the header target to the same source Emitter and properly set that option.

This way users can just do
slangc a.slang -o a.h

source/slang/slang-emit-c-like.cpp Outdated Show resolved Hide resolved
source/slang/slang-emit-c-like.cpp Outdated Show resolved Hide resolved
Comment on lines 1113 to 1123
for (auto c : nameHintDecor->getName()) {
if (c == '.')
sb.append('_');
else {
sb.append(c);
}
}
name = sb.produceString();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this conflict with another name that actually uses '_' as a part of its name?
You may want to check how "__extern_cpp" avoids the name mangling.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I would like to keep namespaces somehow, if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed this to use appendScrubbedName, but it still does basically the same - replaces dot with underscore.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be necessary because generateName(inst) will already call appendScrubbedName for you to generate a valid name for the target. Why do we need a separate branch here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest using __extenr_cpp in user code and remove this special case logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generateName(inst) generates names like _S1, _S2 or something_0, something_1 I wanted to get original names from Slang without any suffixes or changes.

Copy link
Contributor Author

@aroidzap aroidzap May 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Main downside of using __extern_cpp is that it does not support namespaces. Unfortunately I think I would really need to drop this (including auto-transitive __extern_cpp for arguments and struct members), otherwise there is a name mismatch between header and source code - for some reason it worked, but it is not a clear solution. Any ideas how to have __extern_cpp that supports namespaces @csyonghe? Because for example, if I want to have CPU/CUDA code (that switches at runtime if cuda is available), I would have name clashes - and namespaces could solve this for me (if they are different for CPU and CUDA sources).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about this:
we allow __extern_cpp on namespaces as well. And __extern_cpp on a decl means that the decl gets a mangled name that is in the form of: parentNamespaceNameThatHasExternCppModifier_DeclName.

So:

__extern_cpp namespace A
{
         namespace B {
              __extern_cpp struct C;
        }
}
Will have the mangled name `A_C`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this would be nice, thanks.

Unfortunately it probably won't fully solve my problem with linking together CPU and CUDA code.

source/slang/slang-emit-c-like.cpp Outdated Show resolved Hide resolved
@aroidzap
Copy link
Contributor Author

I think the changes here are fine.

We do need to expose these as new target, CppHeader and CudaHeader.

You can keep the isEmittingHeader option in the emitter, just route the header target to the same source Emitter and properly set that option.

This way users can just do slangc a.slang -o a.h

I added hpp,h++,hxx for C++ headers and cuh for CUDA headers, to follow the C++ and CUDA sources extensions already defined in Slang.

@aroidzap aroidzap changed the title WIP: Emit header files for CPP and CUDA targets WIP: Emit header files for cpp and cuda targets Apr 18, 2024
Copy link
Collaborator

@jkwak-work jkwak-work left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to add some tests.

@@ -51,11 +51,13 @@ static const TypeTextUtil::CompileTargetInfo s_compileTargetInfos[] =
{ SLANG_SPIRV_ASM, "spv-asm", "spirv-asm,spirv-assembly", "SPIR-V assembly" },
{ SLANG_C_SOURCE, "c", "c", "C source code" },
{ SLANG_CPP_SOURCE, "cpp,c++,cxx", "cpp,c++,cxx", "C++ source code" },
{ SLANG_CPP_HEADER, "hpp,h++,hxx", "hpp,h++,hxx", "C++ source header" },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be "h,hpp,inc".
I have never seen a file with an extension like "h++" or "hxx".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just don't want to confuse C .h, .inc vs C++ headers.
I also think that .c++ and .h++ is really not common, but maybe .cc and .hh can be added.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with treating .h as cpp header because we won't be supporting c as target anytime soon. Treating cpp as the default is likely the desired behavior anyways.

source/slang/slang-emit-c-like.cpp Outdated Show resolved Hide resolved
Comment on lines 46 to 57
else if (auto arrayType = as<IRArrayType>(type))
{
// TODO: not sure about this
IRBuilder builder(arrayType->getModule());
if (!arrayType->findDecoration<IRHeaderExportDecoration>())
builder.addHeaderExportDecoration(arrayType);

markForHeaderExport_(builder, arrayType->getElementType());
return;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to suggest to write some test cases for this part, then it will become more clear of what needs to happen.

source/slang/slang-ir-inst-defs.h Outdated Show resolved Hide resolved
@csyonghe
Copy link
Collaborator

The PR is showing changes from other recently merged PRs. Make sure you rebase your changes on top of tree, and exclude all the commits that are not part of this change so we can have a clean diff here.

@@ -1111,7 +1113,20 @@ String CLikeSourceEmitter::getName(IRInst* inst)
String name;
if(!m_mapInstToName.tryGetValue(inst, name))
{
name = generateName(inst);
// unmangle names, when emitting header
if (shouldEmitOnlyHeader())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should bottleneck name generating logic through the same branch regardless of shouldEmitOnlyHeader setting so our cpp code can actually match the header code. Is there a reason why we need different behavior then just calling generateName here?

@@ -3526,6 +3546,11 @@ void CLikeSourceEmitter::emitFuncDecorationsImpl(IRFunc* func)

void CLikeSourceEmitter::emitStruct(IRStructType* structType)
{
if (shouldEmitOnlyHeader() && !structType->findDecoration<IRExternCppDecoration>())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of skipping here, we should just make sure we never generate the work to emit the struct from the beginning. See CLikeSourceEmitter::computeEmitActions where we are trying to figure out what insts needs to be emitted. We should just make sure the irrelavent structs are not added. Also if you have:

struct MyType {}

[HeaderExport]
void myFunc(MyType t);

That should result MyType to be emitted automatically. By carefully handling computeEmitActions we should be able to handle this gracefully.

@aroidzap
Copy link
Contributor Author

aroidzap commented May 7, 2024

I have an issue with the #4042 MR and its lowerBuiltinTypesForKernelEntryPoints.

Even though I have something like:

__extern_cpp struct externCppStruct 
{
    __extern_cpp uint2 xy;
};

[CudaKernel]
__extern_cpp void externCppKernel(externCppStruct s) { }

The generated code is now:

struct _VectorStorage_uint2_0
{
    FixedArray<uint, 2>  data_0;
};

struct U_StructStorage_externCppStruct_0
{
    _VectorStorage_uint2_0 xy;
};

__global__ void externCppKernel(U_StructStorage_externCppStruct_0 s_0)
{
    return;
}

Is this expected behavior, even when the struct is marked as __extern_cpp? It is hard to generate useful headers now (the names are not predictable and can change based on other unrelated slang code - especially that number suffix).

Previously it was simply:

struct externCppStruct
{
    uint2  xy;
};

__global__ void externCppKernel(externCppStruct s_0)
{
    return;
}

@csyonghe
Copy link
Collaborator

csyonghe commented May 7, 2024

@saipraveenb25 I think the fix here is that if we are lowering a struct type that has __extern_cpp modifier, we want to transfer the modifier to the lowered type.

@csyonghe
Copy link
Collaborator

csyonghe commented May 7, 2024

@aroidzap If instead we generate:

struct externCppStruct
{
    FixedArray<uint,2> xy;
};

__global__ void externCppKernel(externCppStruct s_0)
{
    return;
}

will that work for you?

@csyonghe
Copy link
Collaborator

csyonghe commented May 7, 2024

The reason here is that we have to lower all types that appear in the parameter list of a CudaKernel to not use the builtin vector types because CUDA builtin vector types has a wider alignment than all other targets, causing mismatch in data layout with C++/HLSL/SPIRV. Our solution is to always lower them into raw arrays.

@aroidzap
Copy link
Contributor Author

aroidzap commented May 8, 2024

Yes itn would help, thanks.

Regarding to the automatic transitive application of __extern_cpp to function arguments and struct members, do you think i should drop it and just put __extern_cpp everywhere in user code? I would need to apply this also in the source (not only header) but this would require taking back the slangc option or slang attribute.

And regarding the namespaces missing in the __extern_cpp, what do you think is the best idea to approach that?

Thank you very much for your help!

@csyonghe
Copy link
Collaborator

csyonghe commented May 8, 2024

I think transitive application to struct members makes sense. I am not sure why we need to apply to argument names, they shouldn't matter, right?

For name mangling, you can check the mangleName function and see how __extern_cpp modifier is handled there.

@aroidzap
Copy link
Contributor Author

aroidzap commented May 8, 2024

I thought that it would be nice to have __extern_cpp automatically propagated to its argument types, so the user don't have to care at all. But having this just for the structs could be nice a compromise.

What about this namespace support for __extern_cpp? Could it be achieved or I should somehow add prefix by some macro in user code (so it can have different prefix for cuda, cpp and possibly metal code).

@aroidzap

This comment was marked as outdated.

@csyonghe
Copy link
Collaborator

csyonghe commented Jun 6, 2024

That looks fine. I don't think there is an existing PR that changes the externCpp behavior for struct fields.

@aroidzap aroidzap changed the title WIP: Emit header files for cpp and cuda targets WIP: Emit header files for cpp, cuda and metal targets Jun 7, 2024
aroidzap and others added 3 commits June 11, 2024 12:44
v2024.1.22

c00f461 remove inline that crashes old glibc version (shader-slang#4398)
fdef653 Improve Direct SPIRV Backend Test Coverage (shader-slang#4396)
33e81a0 [Metal] Fix global constant array emit. (shader-slang#4392)
a38a4fb Make unknown attributes a warning instead of an error. (shader-slang#4391)
a210091 [Metal] Support SV_TargetN. (shader-slang#4390)
2cc9690 Implement for metal `SV_GroupIndex` (shader-slang#4385)
a6b8348 fix the clang/gcc warning (shader-slang#4382)
cfef0c6 Metal: misc fixes and enable more tests. (shader-slang#4374)
2407966 SPIR-V `SV_InstanceId` support in pixel shader (shader-slang#4368)
fba316f Remove `IRHLSLExportDecoration` and `IRKeepAliveDecoration` for non-CUDA/Torch targets (shader-slang#4364)
f0d40ad (origin/master, origin/HEAD, master) capture/replay: implement infrastructure for capture (shader-slang#4372)
ecc6ecb Fix cuda/cpp/metal crash for when using GLSL style shader inputs (shader-slang#4378)
0bf0bf7 Implement Sampler2D for CPP target (shader-slang#4371)
b970b88 Enable full test on macos. (shader-slang#4327)
0574dca Delete glsl_vulkan and glsl_vulkan_one_desc targets. (shader-slang#4361)
085d1a6 Fix emit logic for getElementPtr. (shader-slang#4362)
8813c61 Capability System: Implicit capability upgrade warning/error (shader-slang#4241)
7447fca Add constant folding for % operator. (shader-slang#4359)
8bf7d11 Fix merge error. (shader-slang#4358)
b7e8243 Add slangc flag to `-zero-initialize` all variables (shader-slang#3987)
ccc26c2 Extend the COM-based API to support whole program compilation. (shader-slang#4355)
318adcc Add compiler option to treat enum types as unscoped. (shader-slang#4354)
ec35feb Fix incorrect drop of decoration when translating glsl global var to entrypoint param. (shader-slang#4353)
c194af8 Fix crash on invalid entrypoint varying parameter. (shader-slang#4349)
7a4757d Implicit register binding for hlsl to non-hlsl targets (shader-slang#4338)
180d6b1 Fix duplicate SPIRV decorations. (shader-slang#4346)
fa8c11e Add option to preserve shader parameter declaration in output SPIRV. (shader-slang#4344)
3fe4a77 Fix crash when using optional type in a generic. (shader-slang#4341)
5da06d4 Fix global value inlining for spirv_asm blocks. (shader-slang#4339)
7e79669 [gfx] Metal improvements (shader-slang#4337)
6909d65 SPIRV backend: add support for tessellation stages, (shader-slang#4336)
ef20d93 Test more texture types in Metal (shader-slang#4333)
5a28968 [gfx] Metal texture fixes (shader-slang#4331)
df0a201 Support integer typed textures for GLSL (shader-slang#4329)
51d3585 Remove duplicate `VkPhysicalDeviceComputeShaderDerivativesFeaturesNV` extension structure in vk-api.h (shader-slang#4335)
6d5ef9b Fix `GetAttributeAtVertex` for spirv and glsl targets. (shader-slang#4334)
21bbebb Address glslang ordering requirments for 'derivative_group_*NV' (shader-slang#4323)
72016f9 Partial implementation of static_assert (shader-slang#4294)
712ce65 enable more metal tests (shader-slang#4326)
38c0bac Fix SPIRV emit for `Flat` decoration and TessLevel builtin. (shader-slang#4318)
b5cdd83 Support all integer typed indices in StructuredBuffer Load/Store/[]. (shader-slang#4311)
6857dd5 [gfx] Metal graphics support (shader-slang#4324)
0974463 Fix typos in the docs (shader-slang#4322)
9a23a9a SPIRV `Block` decoration fixes. (shader-slang#4303)
bc680e7 Add initial draft auto-diff basics and IR overview documents (shader-slang#4216)
ee812d1 Disallow certain types of decls in `interface` to provide better diagnostic message. (shader-slang#4312)
65928af Metal system value overhaul. (shader-slang#4308)
e39ceab Adding functional test for GLSL texture functions (shader-slang#4306)
2a45bc3 Support HLSL `.mips` syntax. (shader-slang#4310)
056a4b9 Small SPIRV emit cleanup around vector element extract. (shader-slang#4309)
f83fe55 Make CTS failure report more obvious (shader-slang#4302)
78d34f3 Improve documentation and example formatting consistency (shader-slang#4299)
7c6faf6 Precompute UIntSet from individual capabilities inside generator (shader-slang#4269)
004fe27 Metal compute tests (shader-slang#4292)
72f10a8 Fixed profile string per request in pr#4268 (shader-slang#4297)
a709e02 Update capability-generator-main.cpp (shader-slang#4295)
d301267 Typo in 06-interfaces-generics.md (shader-slang#4284)
fa664d1 Fix build warnings and treat warnings as error on CI (shader-slang#4276)
f149052 Remove unnecessary call to __requireComputeDerivative (shader-slang#4283)
// unmangle names, when emitting header
if (shouldEmitOnlyHeader())
{
if (auto nameHintDecor = inst->findDecoration<IRNameHintDecoration>())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels hacky. We should use the same name emit logic for header and cpp files. For things that needs to be emitted without name mangling, they should be marked as extern_cpp.

@@ -196,6 +196,12 @@ void MetalSourceEmitter::emitParameterGroupImpl(IRGlobalParam* varDecl, IRUnifor

void MetalSourceEmitter::emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPointDecoration* entryPointDecor)
{
if (shouldEmitOnlyHeader())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't use hacks like this. If entry points need to be omitted from header emit, they should be removed from the ir before emitting. We should use a clean up pass before emit to remove entry point functions and all function bodies, then add export decoration to all remaining functions or types so they are not dead code eliminated or ignored by emit.

{

static void addExternCppDecorationTransitively_(IRBuilder & builder, IRInst * inst)
{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use a work list instead of recursive calls to prevent stack overflow.

}
return;
}
else if (auto arrayType = as<IRArrayType>(type))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be IRArrayTypeBase.

Should also handle IRPtrTypeBase, IRHLSLStructuredBufferTyoeBase etc.

// TODO: required? builder.addNameHintDecoration(inst, externCppDecoration->getFunctionName());
// TODO: required? builder.addExternCppDecoration(inst, externCppDecoration->getFunctionName());
// TODO: required? builder.addPublicDecoration(inst);
// TODO: required? builder.addKeepAliveDecoration(inst);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need keep alive and HLSL export.

{
processInst(candidate.inst, candidate.exportDecoration);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should delete all function modules after marking everything.

v2024.1.26

bd01bd3 Fix the type error in kIROp_RWStructuredBufferLoad (shader-slang#4523)
fff79c3 Support HLSL `.sample` operators for MS textures (shader-slang#4524)
6262569 Supply SPIRV capability for textureQueryLod (shader-slang#4522)
6e55043 Error out when constructing tensor views from tensors with 0 stride. (shader-slang#4516)
0e71a6d Resource searching for examples (shader-slang#4518)
d276ea3 Set debug working directory for slangc.exe (shader-slang#4513)
15c02eb Add clearer information about how to submit a proposal document. (shader-slang#4514)
e19e047 Parse scope for local variable declaration (shader-slang#4507)
88382ac Implement CheckAccessFullyMapped (shader-slang#4509)

# Conflicts:
#	source/slang/slang-emit-cuda.h
Slang v2024.10

This release brings support for tuple types, variadic generics and depedent generic constraints.

No breaking changes.

Changes:

e97e7e5 Revert "Fetch slang-llvm.so from correct release (shader-slang#4847)" (shader-slang#4893)
359e96c Proposal: A simpler and more flexible `IDifferentiable` system (shader-slang#4865)
f9f6a28 Support dependent generic constraints. (shader-slang#4870)
03e1e17 Fix `tests\autodiff\reverse-while-loop-3.slang` test (shader-slang#4886)
bcb5391 Exclude synthesized code from code auto documentation system (shader-slang#4889)
6b1b243 Track uninitialized values of `Ptr<Specialize<T>>` inside type `T` without hang (shader-slang#4885)
77e6c64 Fixes shader-slang#4879 (shader-slang#4881)
579d59c Fetch slang-llvm.so from correct release (shader-slang#4847)
d286ff5 Implement Path::createDirectoryRecursive (shader-slang#4871)
f77a5ac Remove using SpvStorageClass values casted into AddressSpace values (shader-slang#4861)
453683b Tuple swizzling, concat, comparison and `countof`. (shader-slang#4856)
ecf85df Variadic Generics Part 2: IR lowering and specialization. (shader-slang#4849)
ca5d303 Make sure to resolve overloaded expr for call args. (shader-slang#4864)
25bc5a3 Avoiding the use of the global AST builder in DeclRefType::create (shader-slang#4866)
b411c05 Include inout cast operation as an aliasing instruction (shader-slang#4859)
9bf5dc9 Design proposal: IFunc interface. (shader-slang#4851)
f447b74 Update documentation for #include to indicate it is for legacy code and new code should use modules (shader-slang#4862)
@aroidzap
Copy link
Contributor Author

aroidzap commented Nov 22, 2024

Hello, I would like to push this forward, I would update to the latest master and try to add some tests.

Are there any updates about this issue - how to handle header files? Is it still feasible to be added and maintained? Or should I
rather stick to some other approach? I would still like to use Slang as compile-time only tool, without any runtime dependencies (reflection api) though.

If this is feasible, should I add just C++ and CUDA headers and add Metal headers etc. in separate MR? Or do all targets at once?

@csyonghe
Copy link
Collaborator

I think the approach of this PR is fine. We just need to address the comments.

@CLAassistant
Copy link

CLAassistant commented Nov 25, 2024

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants