mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 09:23:52 -05:00
Add preliminary Slang support (#7151)
Part of https://github.com/compiler-explorer/compiler-explorer/issues/2331 and similar to [my GLSL change](https://github.com/compiler-explorer/compiler-explorer/pull/6883) [Slang](https://shader-slang.com/) is a GPU focused shading language that has been worked on for years. Last week [The Khronos Group](https://www.khronos.org/news/press/khronos-group-launches-slang-initiative-hosting-open-source-compiler-contributed-by-nvidia) will now be running the project as open governance. The latest [Vulkan SDK](https://www.lunarg.com/lunarg-releases-vulkan-sdk-1-3-296-0-for-windows-linux-macos/) has also added a build of `Slangc` (slang compiler). This change adds support for Slang (the language) as a front end with `Slangc` (the compiler) as the only compiler. Slang can be used for things like GLSL/HLSL, but that is for a future PR. I am in contacts with people on the Slang development and plan to support things for Slang as well as the other GPU related shading languages --- of course, screen shots as well  
This commit is contained in:
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -327,6 +327,12 @@
|
||||
- 'lib/compilers/scala.ts'
|
||||
- 'etc/config/scala.*.properties'
|
||||
|
||||
'lang-slang':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- 'lib/compilers/slang.ts'
|
||||
- 'etc/config/slang.*.properties'
|
||||
|
||||
'lang-solidity':
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
|
||||
13
etc/config/slang.amazon.properties
Normal file
13
etc/config/slang.amazon.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
compilers=&defaultslangc
|
||||
|
||||
defaultCompiler=slangc_2024_14_6
|
||||
supportsBinary=false
|
||||
compilerType=slang
|
||||
instructionSet=spirv
|
||||
|
||||
disassemblerPath=/opt/compiler-explorer/SPIRV-Tools-master/build/tools/spirv-dis
|
||||
|
||||
group.defaultslangc.compilers=slangc_2024_14_6
|
||||
group.defaultslangc.versionFlag=-version
|
||||
compiler.slangc_2024_14_6.exe=/opt/compiler-explorer/slang-2024.14.6/bin/slangc
|
||||
compiler.slangc_2024_14_6.name=slangc 2024.14.6
|
||||
13
etc/config/slang.defaults.properties
Normal file
13
etc/config/slang.defaults.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
compilers=&defaultslangc
|
||||
|
||||
defaultCompiler=slangc_2024_14_6
|
||||
supportsBinary=false
|
||||
compilerType=slang
|
||||
instructionSet=spirv
|
||||
|
||||
disassemblerPath=/opt/compiler-explorer/SPIRV-Tools-master/build/tools/spirv-dis
|
||||
|
||||
group.defaultslangc.compilers=slangc_2024_14_6
|
||||
group.defaultslangc.versionFlag=-version
|
||||
compiler.slangc_2024_14_6.exe=/usr/bin/slangc
|
||||
compiler.slangc_2024_14_6.name=slangc 2024.14.6
|
||||
12
examples/slang/default.slang
Normal file
12
examples/slang/default.slang
Normal file
@@ -0,0 +1,12 @@
|
||||
// Default target is 'spirv' but need to add entry for other targets
|
||||
StructuredBuffer<float> buffer0;
|
||||
StructuredBuffer<float> buffer1;
|
||||
RWStructuredBuffer<float> result;
|
||||
|
||||
[shader("compute")]
|
||||
[numthreads(1,1,1)]
|
||||
void computeMain(uint3 threadId : SV_DispatchThreadID)
|
||||
{
|
||||
uint index = threadId.x;
|
||||
result[index] = buffer0[index] + buffer1[index];
|
||||
}
|
||||
@@ -124,6 +124,7 @@ export {RustcCgGCCCompiler} from './rustc-cg-gcc.js';
|
||||
export {RustCompiler} from './rust.js';
|
||||
export {ScalaCompiler} from './scala.js';
|
||||
export {SdccCompiler} from './sdcc.js';
|
||||
export {SlangCompiler} from './slang.js';
|
||||
export {SolidityCompiler} from './solidity.js';
|
||||
export {SolidityZKsyncCompiler} from './solidity-zksync.js';
|
||||
export {SpiceCompiler} from './spice.js';
|
||||
|
||||
129
lib/compilers/slang.ts
Normal file
129
lib/compilers/slang.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) 2024, Compiler Explorer Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import type {ExecutionOptions} from '../../types/compilation/compilation.interfaces.js';
|
||||
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
|
||||
import {BaseCompiler} from '../base-compiler.js';
|
||||
import {logger} from '../logger.js';
|
||||
import {SPIRVAsmParser} from '../parsers/asm-parser-spirv.js';
|
||||
import * as utils from '../utils.js';
|
||||
|
||||
export class SlangCompiler extends BaseCompiler {
|
||||
protected disassemblerPath: string;
|
||||
protected spirvAsm: SPIRVAsmParser;
|
||||
|
||||
static get key() {
|
||||
return 'slang';
|
||||
}
|
||||
|
||||
constructor(info: any, env: any) {
|
||||
super(info, env);
|
||||
|
||||
this.spirvAsm = new SPIRVAsmParser(this.compilerProps);
|
||||
|
||||
this.disassemblerPath = this.compilerProps<string>('disassemblerPath');
|
||||
}
|
||||
|
||||
getTarget(options?: string[]) {
|
||||
if (options) {
|
||||
const index = options.indexOf('-target');
|
||||
if (index !== -1) {
|
||||
return options[index + 1];
|
||||
}
|
||||
}
|
||||
return 'spirv'; // no target found, slang default to 'spirv'
|
||||
}
|
||||
|
||||
getPrimaryOutputFilename(dirPath: string, outputFilebase: string, target: string) {
|
||||
if (target === 'spirv') {
|
||||
return path.join(dirPath, `${outputFilebase}.spv`);
|
||||
} else {
|
||||
// If there is no intermediate file needed, can use file output
|
||||
return this.getOutputFilename(dirPath, outputFilebase);
|
||||
}
|
||||
}
|
||||
|
||||
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: string, userOptions?: string[]) {
|
||||
const sourceDir = path.dirname(outputFilename);
|
||||
const target = this.getTarget(userOptions);
|
||||
const spvBinFilename = this.getPrimaryOutputFilename(sourceDir, this.outputFilebase, target);
|
||||
return ['-o', spvBinFilename, '-gminimal']; // -g provides debug info
|
||||
}
|
||||
|
||||
override getOutputFilename(dirPath: string, outputFilebase: string) {
|
||||
return path.join(dirPath, `${outputFilebase}.spvasm`);
|
||||
}
|
||||
|
||||
override async runCompiler(
|
||||
compiler: string,
|
||||
options: string[],
|
||||
inputFilename: string,
|
||||
execOptions: ExecutionOptions & {env: Record<string, string>},
|
||||
) {
|
||||
const sourceDir = path.dirname(inputFilename);
|
||||
const target = this.getTarget(options);
|
||||
const slangOutputFilename = this.getPrimaryOutputFilename(sourceDir, this.outputFilebase, target);
|
||||
|
||||
if (!execOptions) {
|
||||
execOptions = this.getDefaultExecOptions();
|
||||
}
|
||||
execOptions.customCwd = path.dirname(inputFilename);
|
||||
|
||||
const slangOutput = await this.exec(compiler, options, execOptions);
|
||||
const result = this.transformToCompilationResult(slangOutput, inputFilename);
|
||||
|
||||
if (slangOutput.code !== 0 || !(await utils.fileExists(slangOutputFilename))) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// While slang does have a way to dump spirv assembly, using spirv-dis will make the SPIR-V we
|
||||
// display unified with other things that produce SPIR-V
|
||||
if (target === 'spirv') {
|
||||
const spvasmFilename = this.getOutputFilename(sourceDir, this.outputFilebase);
|
||||
const disassemblerFlags = [slangOutputFilename, '-o', spvasmFilename, '--comment'];
|
||||
|
||||
const spvasmOutput = await this.exec(this.disassemblerPath, disassemblerFlags, execOptions);
|
||||
if (spvasmOutput.code !== 0) {
|
||||
logger.error('SPIR-V binary to text failed', spvasmOutput);
|
||||
}
|
||||
|
||||
result.stdout = result.stdout.concat(utils.parseOutput(spvasmOutput.stdout));
|
||||
result.stderr = result.stderr.concat(utils.parseOutput(spvasmOutput.stderr));
|
||||
result.languageId = 'spirv';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// slangc defaults to SPIR-V as a target, but has many target it can output
|
||||
override async processAsm(result, filters: ParseFiltersAndOutputOptions, options: string[]) {
|
||||
if (result.asm.startsWith('; SPIR-V')) {
|
||||
return this.spirvAsm.processAsm(result.asm, filters);
|
||||
}
|
||||
// If not SPIR-V, just display as plain text to be safe
|
||||
return super.processAsm(result, filters, options);
|
||||
}
|
||||
}
|
||||
@@ -704,6 +704,17 @@ const definitions: Record<LanguageKey, LanguageDefinition> = {
|
||||
monacoDisassembly: null,
|
||||
digitSeparator: '_',
|
||||
},
|
||||
slang: {
|
||||
name: 'Slang',
|
||||
monaco: 'slang',
|
||||
extensions: ['.slang'],
|
||||
alias: [],
|
||||
logoUrl: 'slang.png',
|
||||
logoUrlDark: null,
|
||||
formatter: null,
|
||||
previewFilter: null,
|
||||
monacoDisassembly: null,
|
||||
},
|
||||
solidity: {
|
||||
name: 'Solidity',
|
||||
monaco: 'sol',
|
||||
|
||||
@@ -105,6 +105,8 @@ export class SPIRVAsmParser extends AsmParser {
|
||||
const opLine = /OpLine/;
|
||||
const opNoLine = /OpNoLine/;
|
||||
const opExtDbg = /OpExtInst\s+%void\s+%\d+\s+Debug/;
|
||||
const opExtDbgLine = /OpExtInst.*DebugLine %\w+\s+(%\w+)\s+(%\w+)/;
|
||||
const opExtDbgNoLine = /OpExtInst.*DebugNoLine/;
|
||||
const opModuleProcessed = /OpModuleProcessed/;
|
||||
const opString = /OpString/;
|
||||
const opSource = /OpSource/;
|
||||
@@ -112,6 +114,13 @@ export class SPIRVAsmParser extends AsmParser {
|
||||
const opMemberName = /OpMemberName/;
|
||||
const opLabel = /OpLabel/;
|
||||
|
||||
// Need to build constants values to get line numbers from DebugInfo.
|
||||
// It is validated these will only be int/uint so can ignore floats
|
||||
const opConstant = /(%\w+)\s+=\s+OpConstant\s+%\w+\s+(\d+)/;
|
||||
const constantIdToValue: Record<string, number> = {};
|
||||
const opDebugSoruce = /(%\w+) = OpExtInst.*DebugSource %\w+\s+(%\w+)/;
|
||||
const idToOpString: Record<string, string> = {};
|
||||
|
||||
const labelDef = /^\s*(%\w+)\s*=\s*(?:OpFunction\s+|OpLabel)/;
|
||||
|
||||
const unclosedString = /^[^"]+"(?:[^"\\]|\\.)*$/;
|
||||
@@ -130,7 +139,29 @@ export class SPIRVAsmParser extends AsmParser {
|
||||
continue;
|
||||
}
|
||||
|
||||
const match = line.match(sourceTag);
|
||||
let match = line.match(opConstant);
|
||||
if (match) {
|
||||
constantIdToValue[match[1]] = parseInt(match[2]);
|
||||
}
|
||||
match = line.match(opDebugSoruce);
|
||||
if (match) {
|
||||
idToOpString[match[1]] = match[2];
|
||||
}
|
||||
|
||||
// TODO - Currently don't handle if there is 'Line End' being used
|
||||
// (most people just have 'Line Start' and 'Line End' the same)
|
||||
match = line.match(opExtDbgLine);
|
||||
if (match) {
|
||||
const opStringId = idToOpString[match[1]];
|
||||
source = {
|
||||
file: utils.maskRootdir(opStrings[parseInt(opStringId)]),
|
||||
line: constantIdToValue[match[1]],
|
||||
mainsource: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Will only have either OpLine or DebugLine
|
||||
match = line.match(sourceTag);
|
||||
if (match) {
|
||||
source = {
|
||||
file: utils.maskRootdir(opStrings[parseInt(match[1])]),
|
||||
@@ -151,7 +182,7 @@ export class SPIRVAsmParser extends AsmParser {
|
||||
}
|
||||
|
||||
// OpLabel are by definition the start of a block, so don't include in previous source
|
||||
if (endBlock.test(line) || opNoLine.test(line) || opLabel.test(line)) {
|
||||
if (endBlock.test(line) || opNoLine.test(line) || opExtDbgNoLine.test(line) || opLabel.test(line)) {
|
||||
source = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ import './nim-mode';
|
||||
import './ocaml-mode';
|
||||
import './openclc-mode';
|
||||
import './ptx-mode';
|
||||
import './slang-mode';
|
||||
import './spice-mode';
|
||||
import './spirv-mode';
|
||||
import './tablegen-mode';
|
||||
|
||||
187
static/modes/slang-mode.ts
Normal file
187
static/modes/slang-mode.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2024, Compiler Explorer Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
import * as monaco from 'monaco-editor';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore "Could not find a declaration file"
|
||||
import * as cpp from 'monaco-editor/esm/vs/basic-languages/cpp/cpp';
|
||||
|
||||
// Currently just a subset of HLSL
|
||||
// Working with Slang dev team to get a unified Monaco set
|
||||
// https://github.com/compiler-explorer/compiler-explorer/issues/7180
|
||||
function definition(): monaco.languages.IMonarchLanguage {
|
||||
const slang = $.extend(true, {}, cpp.language);
|
||||
|
||||
function addKeywords(keywords: string[]) {
|
||||
for (let i = 0; i < keywords.length; ++i) {
|
||||
slang.keywords.push(keywords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function vectorMatrixTypes(basename: string) {
|
||||
const types: string[] = [];
|
||||
for (let i = 1; i !== 5; ++i) {
|
||||
for (let j = 1; j !== 5; ++j) {
|
||||
types.push(`${basename}${i}x${j}`);
|
||||
}
|
||||
types.push(`${basename}${i}`);
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
addKeywords(vectorMatrixTypes('bool'));
|
||||
addKeywords(vectorMatrixTypes('uint'));
|
||||
addKeywords(vectorMatrixTypes('float'));
|
||||
addKeywords(vectorMatrixTypes('int'));
|
||||
|
||||
function resource(name: string) {
|
||||
return [name, `RW${name}`];
|
||||
}
|
||||
|
||||
addKeywords(resource('Buffer'));
|
||||
addKeywords(resource('Texture1D'));
|
||||
addKeywords(resource('Texture1DArray'));
|
||||
addKeywords(resource('Texture2D'));
|
||||
addKeywords(resource('Texture2DArray'));
|
||||
addKeywords(resource('Texture3D'));
|
||||
addKeywords(resource('TextureCube'));
|
||||
addKeywords(resource('TextureCubeArray'));
|
||||
addKeywords(resource('Texture2DMS'));
|
||||
addKeywords(resource('Texture2DMSArray'));
|
||||
addKeywords(resource('ByteAddressBuffer'));
|
||||
addKeywords(resource('StructuredBuffer'));
|
||||
addKeywords(resource('ConstantBuffer'));
|
||||
|
||||
addKeywords([
|
||||
'out',
|
||||
'inout',
|
||||
'vector',
|
||||
'matrix',
|
||||
'uint',
|
||||
|
||||
'SamplerState',
|
||||
'SamplerComparisonState',
|
||||
|
||||
// Additional resource yypes
|
||||
'AppendStructuredBuffer',
|
||||
'ConsumeStructuredBuffer',
|
||||
|
||||
// Intrinsic functions
|
||||
'abort',
|
||||
'abs',
|
||||
'acos',
|
||||
'all',
|
||||
'any',
|
||||
'asin',
|
||||
'asint',
|
||||
'asuint',
|
||||
'atan',
|
||||
'atan2',
|
||||
'ceil',
|
||||
'clamp',
|
||||
'clip',
|
||||
'cos',
|
||||
'cosh',
|
||||
'countbits',
|
||||
'cross',
|
||||
'ddx',
|
||||
'ddx_coarse',
|
||||
'ddx_fine',
|
||||
'ddy',
|
||||
'ddy_coarse',
|
||||
'ddy_fine',
|
||||
'degrees',
|
||||
'determinant',
|
||||
'distance',
|
||||
'dot',
|
||||
'dst',
|
||||
'errorf',
|
||||
'exp',
|
||||
'exp2',
|
||||
'f16tof32',
|
||||
'f32tof16',
|
||||
'faceforward',
|
||||
'firstbithigh',
|
||||
'firstbitlow',
|
||||
'floor',
|
||||
'fma',
|
||||
'fmod',
|
||||
'frac',
|
||||
'frexp',
|
||||
'fwidth',
|
||||
'isfinite',
|
||||
'isinf',
|
||||
'isnan',
|
||||
'ldexp',
|
||||
'length',
|
||||
'lerp',
|
||||
'lit',
|
||||
'log',
|
||||
'log10',
|
||||
'log2',
|
||||
'mad',
|
||||
'max',
|
||||
'min',
|
||||
'modf',
|
||||
'msad4',
|
||||
'mul',
|
||||
'noise',
|
||||
'normalize',
|
||||
'pow',
|
||||
'radians',
|
||||
'rcp',
|
||||
'reflect',
|
||||
'refract',
|
||||
'reversebits',
|
||||
'round',
|
||||
'rsqrt',
|
||||
'saturate',
|
||||
'sign',
|
||||
'sin',
|
||||
'sincos',
|
||||
'sinh',
|
||||
'smoothstep',
|
||||
'sqrt',
|
||||
'step',
|
||||
'tan',
|
||||
'tanh',
|
||||
'transpose',
|
||||
'trunc',
|
||||
|
||||
// Compute + mesh/amplification shaders
|
||||
'numthreads',
|
||||
'outputtopology',
|
||||
'DispatchMesh',
|
||||
'groupshared',
|
||||
]);
|
||||
|
||||
return slang;
|
||||
}
|
||||
|
||||
monaco.languages.register({id: 'slang'});
|
||||
monaco.languages.setMonarchTokensProvider('slang', definition());
|
||||
|
||||
export {};
|
||||
@@ -83,6 +83,7 @@ export type LanguageKey =
|
||||
| 'ruby'
|
||||
| 'rust'
|
||||
| 'scala'
|
||||
| 'slang'
|
||||
| 'solidity'
|
||||
| 'spice'
|
||||
| 'spirv'
|
||||
|
||||
BIN
views/resources/logos/slang.png
Normal file
BIN
views/resources/logos/slang.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
Reference in New Issue
Block a user