mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
178 lines
6.6 KiB
TypeScript
178 lines
6.6 KiB
TypeScript
// Copyright (c) 2025, 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 fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
|
|
import {CompilationResult, ExecutionOptionsWithEnv} from '../../types/compilation/compilation.interfaces.js';
|
|
import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js';
|
|
import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js';
|
|
import {SelectedLibraryVersion} from '../../types/libraries/libraries.interfaces.js';
|
|
import {BaseCompiler} from '../base-compiler.js';
|
|
import {CompilationEnvironment} from '../compilation-env.js';
|
|
import * as utils from '../utils.js';
|
|
|
|
export class SailCompiler extends BaseCompiler {
|
|
// Path to C compiler to use to compile generated C code to binary.
|
|
private readonly cCompiler: string;
|
|
|
|
static get key() {
|
|
return 'sail';
|
|
}
|
|
|
|
constructor(info: PreliminaryCompilerInfo, env: CompilationEnvironment) {
|
|
super(info, env);
|
|
|
|
this.outputFilebase = 'model';
|
|
this.cCompiler = this.compilerProps<string>('cCompiler');
|
|
console.assert(this.cCompiler !== undefined, 'cCompiler not set for Sail compiler');
|
|
}
|
|
|
|
override optionsForFilter(filters: ParseFiltersAndOutputOptions, outputFilename: any) {
|
|
// Target C backend (and override the default C options, -g etc.).
|
|
return ['-c'];
|
|
}
|
|
|
|
override async runCompiler(
|
|
compiler: string,
|
|
options: string[],
|
|
inputFilename: string,
|
|
execOptions: ExecutionOptionsWithEnv,
|
|
filters?: ParseFiltersAndOutputOptions,
|
|
): Promise<CompilationResult> {
|
|
if (!execOptions) {
|
|
execOptions = this.getDefaultExecOptions();
|
|
}
|
|
|
|
const tmpDir = path.dirname(inputFilename);
|
|
|
|
if (!execOptions.customCwd) {
|
|
execOptions.customCwd = tmpDir;
|
|
}
|
|
|
|
const fullResult: CompilationResult = {
|
|
code: 0,
|
|
timedOut: false,
|
|
stdout: [],
|
|
stderr: [],
|
|
buildsteps: [],
|
|
inputFilename,
|
|
};
|
|
|
|
const sailResult = await this.doBuildstepAndAddToResult(
|
|
fullResult,
|
|
'Sail to C',
|
|
compiler,
|
|
[...options, '-o', this.outputFilebase],
|
|
execOptions,
|
|
);
|
|
|
|
const binary = filters?.binary === true;
|
|
|
|
if (sailResult.code !== 0 || !binary) {
|
|
return fullResult;
|
|
}
|
|
|
|
const outputFilenameC = this.getOutputFilename(tmpDir, this.outputFilebase);
|
|
const outputFilenameExe = outputFilenameC + '.exe';
|
|
|
|
fullResult.executableFilename = outputFilenameExe;
|
|
|
|
// Query the sail compiler via `sail -dir` to find out where the
|
|
// C runtime files are (`sail.c` etc).
|
|
const sailDirResult = await this.doBuildstepAndAddToResult(
|
|
fullResult,
|
|
'Get Sail dir',
|
|
compiler,
|
|
['-dir'],
|
|
execOptions,
|
|
);
|
|
|
|
if (sailDirResult.code !== 0) {
|
|
return fullResult;
|
|
}
|
|
|
|
const sailDir = sailDirResult.stdout
|
|
.map(line => line.text)
|
|
.join('\n')
|
|
.trim();
|
|
|
|
// Now compile the C file to an executable.
|
|
const compileResult = await this.doBuildstepAndAddToResult(
|
|
fullResult,
|
|
'C to binary',
|
|
this.cCompiler,
|
|
[
|
|
outputFilenameC,
|
|
// Sail C support files.
|
|
'-I',
|
|
`${sailDir}/lib`,
|
|
`${sailDir}/lib/elf.c`,
|
|
`${sailDir}/lib/rts.c`,
|
|
`${sailDir}/lib/sail.c`,
|
|
`${sailDir}/lib/sail_failure.c`,
|
|
// Support for .elf.gz files. This has been removed in Sail 0.19
|
|
// so it isn't needed for future versions.
|
|
'-lz',
|
|
// For arbitrary precision integer types.
|
|
'-lgmp',
|
|
// Enable optimisations so the assembly isn't hilariously verbose.
|
|
// Ideally this would be user configurable but we'd need
|
|
// something like `-Wl,..` to pass options through Sail to
|
|
// here and that doesn't really exist.
|
|
'-O',
|
|
'-o',
|
|
outputFilenameExe,
|
|
],
|
|
execOptions,
|
|
);
|
|
|
|
// This is weird, but as far as I can tell CE expects the same
|
|
// output file name when compiling to IR (C in this case)
|
|
// and when compiling to a binary. If you don't do this it
|
|
// tries to do nonsensical things like objdumping the C, so we
|
|
// copy the binary back over the C file.
|
|
if (compileResult.code === 0 && (await utils.fileExists(outputFilenameExe))) {
|
|
console.log(`Copying ${outputFilenameExe} to ${outputFilenameC}`);
|
|
await fs.copyFile(outputFilenameExe, outputFilenameC);
|
|
}
|
|
|
|
return fullResult;
|
|
}
|
|
|
|
override getOutputFilename(dirPath: string, outputFilebase: string, key?: any): string {
|
|
return path.join(dirPath, `${outputFilebase}.c`);
|
|
}
|
|
|
|
override getLibLinkInfo(
|
|
filters: ParseFiltersAndOutputOptions,
|
|
libraries: SelectedLibraryVersion[],
|
|
toolchainPath: string,
|
|
dirPath: string,
|
|
) {
|
|
// Prevent any library linking flags from being passed to Sail during compilation.
|
|
return {libLinks: [], libPathsAsFlags: [], staticLibLinks: []};
|
|
}
|
|
}
|