diff options
author | J. Ryan Stinnett <jryans@gmail.com> | 2023-12-12 01:35:45 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-11 19:35:45 -0600 |
commit | f0e1d5d26490b4a62aaee35b4cfc700d6e3f8a1c (patch) | |
tree | b238d2e81495603183d34adb65b6b33a715f1c25 /lib/compilers/racket.ts | |
parent | 7d3156ae5ffe62a9f8b9188ea7f8c92276f526a1 (diff) | |
download | compiler-explorer-f0e1d5d26490b4a62aaee35b4cfc700d6e3f8a1c.tar.gz compiler-explorer-f0e1d5d26490b4a62aaee35b4cfc700d6e3f8a1c.zip |
Add Racket optimisation pipeline (#5836)gh-9905
This adds a Racket optimisation pipeline view by reusing the existing
LLVM-focused optimisation pipeline UI. A Racket-specific pass parser
translates its output into passes for the UI to present.
This new Racket optimisation pipeline view is currently only enabled for
Racket nightly, as it depends on [recent
changes](https://github.com/racket/racket/pull/4842) to Racket's
compiler output to function.
This also extends the opt pipeline view to allow customising the
function selector label as well as the options and filters for each
compiler where needed.

---------
Co-authored-by: Matt Godbolt <matt@godbolt.org>
Diffstat (limited to 'lib/compilers/racket.ts')
-rw-r--r-- | lib/compilers/racket.ts | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/lib/compilers/racket.ts b/lib/compilers/racket.ts index 0811058d1..57a4fb0bf 100644 --- a/lib/compilers/racket.ts +++ b/lib/compilers/racket.ts @@ -24,14 +24,22 @@ import path from 'path'; +import fs from 'fs-extra'; + import type {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; +import type { + OptPipelineBackendOptions, + OptPipelineOutput, +} from '../../types/compilation/opt-pipeline-output.interfaces.js'; import {BaseCompiler} from '../base-compiler.js'; import {logger} from '../logger.js'; +import {RacketPassDumpParser} from '../parsers/racket-pass-dump-parser.js'; export class RacketCompiler extends BaseCompiler { private raco: string; + private passDumpParser: RacketPassDumpParser; static get key() { return 'racket'; @@ -46,7 +54,17 @@ export class RacketCompiler extends BaseCompiler { }, env, ); + // Revise this if we add released versions of Racket 8.12 or later + if (this.compiler.isNightly) { + this.compiler.optPipeline = { + groupName: 'Linklet', + // Disable all options and filters, currently unsupported + supportedOptions: [], + supportedFilters: [], + }; + } this.raco = this.compilerProps<string>(`compiler.${this.compiler.id}.raco`); + this.passDumpParser = new RacketPassDumpParser(this.compilerProps); } override optionsForFilter( @@ -62,6 +80,10 @@ export class RacketCompiler extends BaseCompiler { return []; } + override isCfgCompiler(/*compilerVersion*/) { + return false; + } + override supportsObjdump(): boolean { return true; } @@ -70,6 +92,28 @@ export class RacketCompiler extends BaseCompiler { return []; } + override orderArguments( + options: string[], + inputFilename: string, + libIncludes: string[], + libOptions: string[], + libPaths: string[], + libLinks: string[], + userOptions: string[], + staticLibLinks: string[], + ) { + // Move input file to end of options + return options.concat( + userOptions, + libIncludes, + libOptions, + libPaths, + libLinks, + staticLibLinks, + [this.filename(inputFilename)], + ); + } + override async runCompiler( compiler: string, options: string[], @@ -85,7 +129,14 @@ export class RacketCompiler extends BaseCompiler { } // Compile to bytecode via `raco make` + options = [...options]; options.unshift('make'); + + // Replace input filename (which may be different than the default) + // as in pipeline mode below + options.pop(); + options.push(inputFilename); + const makeResult = await this.exec(this.raco, options, execOptions); return this.transformToCompilationResult(makeResult, inputFilename); @@ -129,4 +180,88 @@ export class RacketCompiler extends BaseCompiler { asm: [{text: result.asm}], }; } + + override async generateOptPipeline( + inputFilename: string, + options: string[], + filters: ParseFiltersAndOutputOptions, + optPipelineOptions: OptPipelineBackendOptions, + ): Promise<OptPipelineOutput | undefined> { + // Use a separate directory so this is not affected by the main + // compilation (which races in parallel) + const pipelineDir = await this.newTempDir(); + const inputFile = this.filename(inputFilename); + const pipelineFile = path.join(pipelineDir, path.basename(inputFile)); + await fs.copyFile(inputFile, pipelineFile); + + const execOptions = this.getDefaultExecOptions(); + execOptions.maxOutput = 1024 * 1024 * 1024; + + // Dump various optimisation passes during compilation + execOptions.env['PLT_LINKLET_SHOW_CP0'] = '1'; + execOptions.env['PLT_LINKLET_SHOW_PASSES'] = 'all'; + execOptions.env['PLT_LINKLET_SHOW_ASSEMBLY'] = '1'; + + const compileStart = performance.now(); + const output = await this.runCompiler( + this.compiler.exe, + options, + pipelineFile, + execOptions, + ); + const compileEnd = performance.now(); + + if (output.timedOut) { + return { + error: 'Racket invocation timed out', + results: {}, + compileTime: output.execTime || compileEnd - compileStart, + }; + } + + if (output.code !== 0) { + return; + } + + // Useful for local debugging + // const passesFile = path.join(pipelineDir, 'passes.scm'); + // console.log('Passes:', passesFile); + // await fs.writeFile(passesFile, output.stderr.map(l => l.text).join('\n')); + + try { + const parseStart = performance.now(); + const llvmOptPipeline = await this.processOptPipeline( + output, + filters, + optPipelineOptions, + /* debugPatched = */ false, + ); + const parseEnd = performance.now(); + + return { + results: llvmOptPipeline, + compileTime: compileEnd - compileStart, + parseTime: parseEnd - parseStart, + }; + } catch (e: any) { + return { + error: e.toString(), + results: {}, + compileTime: compileEnd - compileStart, + }; + } + } + + override async processOptPipeline( + output, + filters: ParseFiltersAndOutputOptions, + optPipelineOptions: OptPipelineBackendOptions, + debugPatched?: boolean, + ) { + return this.passDumpParser.process( + output.stderr, + filters, + optPipelineOptions, + ); + } } |