diff options
author | Jeremy Rifkin <51220084+jeremy-rifkin@users.noreply.github.com> | 2023-06-11 19:10:30 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-06-11 19:10:30 -0400 |
commit | 60ce06b02f8ece8bbbecb26cb69f9ad45e75aa16 (patch) | |
tree | a10d1bd160dd06f5b191adb1ad5cd12403790d48 | |
parent | 910d69f233414ebfb0b680e012f44ced14505016 (diff) | |
download | compiler-explorer-gh-7689.tar.gz compiler-explorer-gh-7689.zip |
Improve cache handling on the frontend, cache executions on the backend, and improve controls on the exec pane (#5111)gh-7689
-rw-r--r-- | .eslintrc.yml | 2 | ||||
-rw-r--r-- | docs/API.md | 15 | ||||
-rw-r--r-- | lib/base-compiler.ts | 69 | ||||
-rw-r--r-- | lib/compilers/java.ts | 3 | ||||
-rw-r--r-- | lib/compilers/kotlin.ts | 3 | ||||
-rw-r--r-- | lib/compilers/win32-mingw-clang.ts | 11 | ||||
-rw-r--r-- | lib/compilers/win32-mingw-gcc.ts | 11 | ||||
-rw-r--r-- | lib/handlers/compile.interfaces.ts | 6 | ||||
-rw-r--r-- | lib/handlers/compile.ts | 25 | ||||
-rw-r--r-- | lib/llvm-ir.ts | 4 | ||||
-rw-r--r-- | static/compiler-service.ts | 6 | ||||
-rw-r--r-- | static/panes/compiler-request.interfaces.ts | 68 | ||||
-rw-r--r-- | static/panes/compiler.ts | 19 | ||||
-rw-r--r-- | static/panes/device-view.ts | 1 | ||||
-rw-r--r-- | static/panes/executor.ts | 41 | ||||
-rw-r--r-- | static/styles/explorer.scss | 5 | ||||
-rw-r--r-- | static/styles/themes/dark-theme.scss | 12 | ||||
-rw-r--r-- | static/styles/themes/default-theme.scss | 12 | ||||
-rw-r--r-- | static/styles/themes/pink-theme.scss | 12 | ||||
-rw-r--r-- | test/handlers/compile-tests.js | 3 | ||||
-rw-r--r-- | types/compilation/compilation.interfaces.ts | 87 | ||||
-rw-r--r-- | types/compiler.interfaces.ts | 3 | ||||
-rw-r--r-- | types/features/filters.interfaces.ts | 30 | ||||
-rw-r--r-- | views/templates/panes/executor.pug | 6 |
24 files changed, 274 insertions, 180 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml index 87e46521b..f1f890528 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -44,7 +44,7 @@ rules: import/first: error import/newline-after-import: error import/no-absolute-path: error - import/no-cycle: error + #import/no-cycle: error # TODO(jeremy-rifkin) disabled for now due to compilation types import/no-default-export: error import/no-deprecated: error import/no-mutable-exports: error diff --git a/docs/API.md b/docs/API.md index d95adc06d..d96707e78 100644 --- a/docs/API.md +++ b/docs/API.md @@ -121,7 +121,20 @@ The filters are a JSON object with `true`/`false` values. If not supplied, defau filters override their default values. The `compilerOptions` is used to pass extra arguments to the back end, and is probably not useful for most REST users. -To force a cache bypass, set `bypassCache` in the root of the request to `true`. +To force a cache bypass, `bypassCache` can be set. This accepts an enum value according to: + +```ts +export enum BypassCache { + None = 0, + Compilation = 1, + Execution = 2, +} +``` + +If bypass compile cache is specified and an execution is to happen, the execution cache will also be bypassed. + +Note: `bypassCache` previously accepted a boolean. The enum values have been carefully chosen for backwards +compatibility. Filters include `binary`, `binaryObject`, `labels`, `intel`, `directives` and `demangle`, which correspond to the UI buttons on the HTML version. diff --git a/lib/base-compiler.ts b/lib/base-compiler.ts index d72adeda6..77aff9750 100644 --- a/lib/base-compiler.ts +++ b/lib/base-compiler.ts @@ -29,15 +29,18 @@ import * as PromClient from 'prom-client'; import temp from 'temp'; import _ from 'underscore'; -import type { +import { BufferOkFunc, BuildResult, BuildStep, + BypassCache, CompilationCacheKey, CompilationInfo, CompilationResult, CustomInputForTool, ExecutionOptions, + bypassCompilationCache, + bypassExecutionCache, } from '../types/compilation/compilation.interfaces.js'; import type { LLVMOptPipelineBackendOptions, @@ -489,7 +492,7 @@ export class BaseCompiler implements ICompiler { maxSize: number, intelAsm, demangle, - staticReloc: boolean, + staticReloc: boolean | undefined, dynamicReloc: boolean, filters: ParseFiltersAndOutputOptions, ) { @@ -1718,11 +1721,13 @@ export class BaseCompiler implements ICompiler { }; } - async getOrBuildExecutable(key) { + async getOrBuildExecutable(key, bypassCache: BypassCache) { const dirPath = await this.newTempDir(); - const buildResults = await this.loadPackageWithExecutable(key, dirPath); - if (buildResults) return buildResults; + if (!bypassCompilationCache(bypassCache)) { + const buildResults = await this.loadPackageWithExecutable(key, dirPath); + if (buildResults) return buildResults; + } let compilationResult; try { @@ -1843,9 +1848,11 @@ export class BaseCompiler implements ICompiler { }; } - async handleExecution(key, executeParameters): Promise<CompilationResult> { - if (this.compiler.interpreted) return this.handleInterpreting(key, executeParameters); - const buildResult = await this.getOrBuildExecutable(key); + async doExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { + if (this.compiler.interpreted) { + return this.handleInterpreting(key, executeParameters); + } + const buildResult = await this.getOrBuildExecutable(key, bypassCache); if (buildResult.code !== 0) { return { code: -1, @@ -1892,6 +1899,23 @@ export class BaseCompiler implements ICompiler { }; } + async handleExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { + // stringify now so shallow copying isn't a problem, I think the executeParameters get modified + const execKey = JSON.stringify({key, executeParameters}); + if (!bypassExecutionCache(bypassCache)) { + const cacheResult = await this.env.cacheGet(execKey as any); + if (cacheResult) { + return cacheResult; + } + } + + const result = await this.doExecution(key, executeParameters, bypassCache); + if (!bypassExecutionCache(bypassCache)) { + await this.env.cachePut(execKey, result, undefined); + } + return result; + } + getCacheKey(source, options, backendOptions, filters, tools, libraries, files) { return {compiler: this.compiler, source, options, backendOptions, filters, tools, libraries, files}; } @@ -2262,7 +2286,7 @@ export class BaseCompiler implements ICompiler { } } - async cmake(files, key) { + async cmake(files, key, bypassCache: BypassCache) { // key = {source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries}; if (!this.compiler.supportsBinary) { @@ -2300,7 +2324,9 @@ export class BaseCompiler implements ICompiler { const outputFilename = this.getExecutableFilename(path.join(dirPath, 'build'), this.outputFilebase, key); - let fullResult = await this.loadPackageWithExecutable(cacheKey, dirPath); + let fullResult = !bypassExecutionCache(bypassCache) + ? await this.loadPackageWithExecutable(cacheKey, dirPath) + : null; if (fullResult) { fullResult.fetchedFromCache = true; @@ -2421,6 +2447,7 @@ export class BaseCompiler implements ICompiler { cacheKey.filters, libsAndOptions.options, optOutput, + bypassCache, path.join(dirPath, 'build'), ); @@ -2469,7 +2496,17 @@ export class BaseCompiler implements ICompiler { } } - async compile(source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries, files) { + async compile( + source, + options, + backendOptions, + filters, + bypassCache: BypassCache, + tools, + executionParameters, + libraries, + files, + ) { const optionsError = this.checkOptions(options); if (optionsError) throw optionsError; const sourceError = this.checkSource(source); @@ -2494,7 +2531,7 @@ export class BaseCompiler implements ICompiler { filters = Object.assign({}, filters); filters.execute = false; - if (!bypassCache) { + if (!bypassCompilationCache(bypassCache)) { const cacheRetreiveTimeStart = process.hrtime.bigint(); // TODO: We should be able to eliminate this any cast. `key` should be cacheable (if it's not that's a big // problem) Because key coantains a CompilerInfo which contains a function member it can't be assigned to a @@ -2512,7 +2549,7 @@ export class BaseCompiler implements ICompiler { result.execResult = await this.env.enqueue(async () => { const start = performance.now(); executionQueueTimeHistogram.observe((start - queueTime) / 1000); - const res = await this.handleExecution(key, executeParameters); + const res = await this.handleExecution(key, executeParameters, bypassCache); executionTimeHistogram.observe((performance.now() - start) / 1000); return res; }); @@ -2532,7 +2569,7 @@ export class BaseCompiler implements ICompiler { source = this.preProcess(source, filters); if (backendOptions.executorRequest) { - const execResult = await this.handleExecution(key, executeParameters); + const execResult = await this.handleExecution(key, executeParameters, bypassCache); if (execResult && execResult.buildResult) { this.doTempfolderCleanup(execResult.buildResult); } @@ -2570,6 +2607,7 @@ export class BaseCompiler implements ICompiler { filters, options, optOutput, + bypassCache, ); })(); compilationTimeHistogram.observe((performance.now() - start) / 1000); @@ -2587,10 +2625,11 @@ export class BaseCompiler implements ICompiler { filters, options, optOutput, + bypassCache: BypassCache, customBuildPath?, ) { // Start the execution as soon as we can, but only await it at the end. - const execPromise = doExecute ? this.handleExecution(key, executeParameters) : null; + const execPromise = doExecute ? this.handleExecution(key, executeParameters, bypassCache) : null; if (result.hasOptOutput) { delete result.optPath; diff --git a/lib/compilers/java.ts b/lib/compilers/java.ts index 320469f6e..c01940c57 100644 --- a/lib/compilers/java.ts +++ b/lib/compilers/java.ts @@ -35,6 +35,7 @@ import {logger} from '../logger.js'; import * as utils from '../utils.js'; import {JavaParser} from './argument-parsers.js'; +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; export class JavaCompiler extends BaseCompiler { static get key() { @@ -128,7 +129,7 @@ export class JavaCompiler extends BaseCompiler { } override async handleInterpreting(key, executeParameters) { - const compileResult = await this.getOrBuildExecutable(key); + const compileResult = await this.getOrBuildExecutable(key, BypassCache.None); if (compileResult.code === 0) { executeParameters.args = [ '-Xss136K', // Reduce thread stack size diff --git a/lib/compilers/kotlin.ts b/lib/compilers/kotlin.ts index 0b5cce585..9b1bab103 100644 --- a/lib/compilers/kotlin.ts +++ b/lib/compilers/kotlin.ts @@ -22,6 +22,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; import type {PreliminaryCompilerInfo} from '../../types/compiler.interfaces.js'; import type {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; @@ -98,7 +99,7 @@ export class KotlinCompiler extends JavaCompiler { ...key, options: ['-include-runtime', '-d', 'example.jar'], }; - const compileResult = await this.getOrBuildExecutable(alteredKey); + const compileResult = await this.getOrBuildExecutable(alteredKey, BypassCache.None); executeParameters.args = [ '-Xss136K', // Reduce thread stack size '-XX:CICompilerCount=2', // Reduce JIT compilation threads. 2 is minimum diff --git a/lib/compilers/win32-mingw-clang.ts b/lib/compilers/win32-mingw-clang.ts index 83c98c52e..47a6159a8 100644 --- a/lib/compilers/win32-mingw-clang.ts +++ b/lib/compilers/win32-mingw-clang.ts @@ -24,7 +24,12 @@ import path from 'path'; -import {BuildResult, CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import { + BuildResult, + BypassCache, + CompilationResult, + ExecutionOptions, +} from '../../types/compilation/compilation.interfaces.js'; import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {copyNeededDlls} from '../win-utils.js'; @@ -102,8 +107,8 @@ export class Win32MingWClang extends ClangCompiler { return result; } - override async handleExecution(key, executeParameters): Promise<CompilationResult> { + override async handleExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { const execOptions = this.getDefaultExecOptions(); - return super.handleExecution(key, {...executeParameters, env: execOptions.env}); + return super.handleExecution(key, {...executeParameters, env: execOptions.env}, bypassCache); } } diff --git a/lib/compilers/win32-mingw-gcc.ts b/lib/compilers/win32-mingw-gcc.ts index 4e3106bc5..04b43f985 100644 --- a/lib/compilers/win32-mingw-gcc.ts +++ b/lib/compilers/win32-mingw-gcc.ts @@ -24,7 +24,12 @@ import path from 'path'; -import {BuildResult, CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces.js'; +import { + BuildResult, + BypassCache, + CompilationResult, + ExecutionOptions, +} from '../../types/compilation/compilation.interfaces.js'; import {ParseFiltersAndOutputOptions} from '../../types/features/filters.interfaces.js'; import {copyNeededDlls} from '../win-utils.js'; @@ -102,8 +107,8 @@ export class Win32MingWGcc extends GCCCompiler { return result; } - override async handleExecution(key, executeParameters): Promise<CompilationResult> { + override async handleExecution(key, executeParameters, bypassCache: BypassCache): Promise<CompilationResult> { const execOptions = this.getDefaultExecOptions(); - return super.handleExecution(key, {...executeParameters, env: execOptions.env}); + return super.handleExecution(key, {...executeParameters, env: execOptions.env}, bypassCache); } } diff --git a/lib/handlers/compile.interfaces.ts b/lib/handlers/compile.interfaces.ts index 104450b44..af4204066 100644 --- a/lib/handlers/compile.interfaces.ts +++ b/lib/handlers/compile.interfaces.ts @@ -22,6 +22,8 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; + // IF YOU MODIFY ANYTHING HERE PLEASE UPDATE THE DOCUMENTATION! // This type models a request so all fields must be optional strings. @@ -52,12 +54,12 @@ export type CompilationRequestArgs = { export type CompileRequestJsonBody = { options: CompilationRequestArgs; source: string; - bypassCache: boolean; + bypassCache: BypassCache; }; export type CompileRequestTextBody = { source: string; - bypassCache: boolean; + bypassCache: BypassCache; options: any; userArguments: string; executeParametersArgs: any; diff --git a/lib/handlers/compile.ts b/lib/handlers/compile.ts index ac19c7972..6ec2782f6 100644 --- a/lib/handlers/compile.ts +++ b/lib/handlers/compile.ts @@ -50,6 +50,7 @@ import { } from './compile.interfaces.js'; import {remove} from '../common-utils.js'; import {CompilerOverrideOptions} from '../../types/compilation/compiler-overrides.interfaces.js'; +import {BypassCache, CompileChildLibraries, ExecutionParams} from '../../types/compilation/compilation.interfaces.js'; import {SentryCapture} from '../sentry.js'; temp.track(); @@ -82,20 +83,15 @@ function initialise(compilerEnv: CompilationEnvironment) { }, tempDirCleanupSecs * 1000); } -export type ExecutionParams = { - args: string[]; - stdin: string; -}; - type ParsedRequest = { source: string; options: string[]; backendOptions: Record<string, any>; filters: ParseFiltersAndOutputOptions; - bypassCache: boolean; + bypassCache: BypassCache; tools: any; executionParameters: ExecutionParams; - libraries: any[]; + libraries: CompileChildLibraries[]; }; export class CompileHandler { @@ -353,7 +349,7 @@ export class CompileHandler { options: string, backendOptions: Record<string, any> = {}, filters: ParseFiltersAndOutputOptions, - bypassCache = false, + bypassCache = BypassCache.None, tools; const execReqParams: ExecutionRequestParams = {}; let libraries: any[] = []; @@ -363,7 +359,7 @@ export class CompileHandler { const jsonRequest = this.checkRequestRequirements(req); const requestOptions = jsonRequest.options; source = jsonRequest.source; - if (jsonRequest.bypassCache) bypassCache = true; + if (jsonRequest.bypassCache) bypassCache = jsonRequest.bypassCache; options = requestOptions.userArguments; const execParams = requestOptions.executeParameters || {}; execReqParams.args = execParams.args; @@ -375,7 +371,7 @@ export class CompileHandler { } else if (req.body && req.body.compiler) { const textRequest = req.body as CompileRequestTextBody; source = textRequest.source; - if (textRequest.bypassCache) bypassCache = true; + if (textRequest.bypassCache) bypassCache = textRequest.bypassCache; options = textRequest.userArguments; execReqParams.args = textRequest.executeParametersArgs; execReqParams.stdin = textRequest.executeParametersStdin; @@ -423,6 +419,11 @@ export class CompileHandler { for (const tool of tools) { tool.args = utils.splitArguments(tool.args); } + + // Backwards compatibility: bypassCache used to be a boolean. + // Convert a boolean input to an enum's underlying numeric value + bypassCache = 1 * bypassCache; + return { source, options: utils.splitArguments(options), @@ -497,7 +498,9 @@ export class CompileHandler { this.cmakeCounter.inc({language: compiler.lang.id}); const options = this.parseRequest(req, compiler); compiler - .cmake(req.body.files, options) + // Backwards compatibility: bypassCache used to be a boolean. + // Convert a boolean input to an enum's underlying numeric value + .cmake(req.body.files, options, req.body.bypassCache * 1) .then(result => { if (result.didExecute || (result.execResult && result.execResult.didExecute)) this.cmakeExecuteCounter.inc({language: compiler.lang.id}); diff --git a/lib/llvm-ir.ts b/lib/llvm-ir.ts index 5bdf7e651..4ddf9e58f 100644 --- a/lib/llvm-ir.ts +++ b/lib/llvm-ir.ts @@ -249,9 +249,9 @@ export class LlvmIrParser { if (_.isString(ir)) { return await this.processIr(ir, { filterDebugInfo: !!filters.debugCalls, - filterIRMetadata: filters.directives, + filterIRMetadata: !!filters.directives, filterAttributes: false, - demangle: filters.demangle, + demangle: !!filters.demangle, // discard value names is handled earlier }); } diff --git a/static/compiler-service.ts b/static/compiler-service.ts index 1fb435c43..89120dbc2 100644 --- a/static/compiler-service.ts +++ b/static/compiler-service.ts @@ -44,7 +44,7 @@ const ASCII_COLORS_RE = new RegExp(/\x1B\[[\d;]*m(.\[K)?/g); export class CompilerService { private readonly base = window.httpRoot; private allowStoreCodeDebug: boolean; - cache: LRUCache<string, CompilationResult>; + private cache: LRUCache<string, CompilationResult>; private readonly compilersByLang: Record<string, Record<string, CompilerInfo>>; constructor(eventHub: EventEmitter) { @@ -214,7 +214,7 @@ export class CompilerService { public async submit(request: Record<string, any>) { request.allowStoreCodeDebug = this.allowStoreCodeDebug; const jsonRequest = JSON.stringify(request); - if (options.doCache) { + if (options.doCache && !request.bypassCache) { const cachedResult = this.cache.get(jsonRequest); if (cachedResult) { return { @@ -252,7 +252,7 @@ export class CompilerService { public submitCMake(request: Record<string, any>) { request.allowStoreCodeDebug = this.allowStoreCodeDebug; const jsonRequest = JSON.stringify(request); - if (options.doCache) { + if (options.doCache && !request.bypassCache) { const cachedResult = this.cache.get(jsonRequest); if (cachedResult) { return Promise.resolve({ diff --git a/static/panes/compiler-request.interfaces.ts b/static/panes/compiler-request.interfaces.ts index 9c931e678..69fb390a8 100644 --- a/static/panes/compiler-request.interfaces.ts +++ b/static/panes/compiler-request.interfaces.ts @@ -22,77 +22,9 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -import type {LLVMOptPipelineBackendOptions} from '../../types/compilation/llvm-opt-pipeline-output.interfaces.js'; -import type {PPOptions} from './pp-view.interfaces.js'; -import type {GccDumpViewSelectedPass} from './gccdump-view.interfaces.js'; -import type {FiledataPair} from '../../types/compilation/compilation.interfaces.js'; -import type {ConfiguredOverrides} from '../compilation/compiler-overrides.interfaces.js'; -import {LLVMIrBackendOptions} from '../compilation/ir.interfaces.js'; - -export type ActiveTools = { - id: number; - args: string[]; - stdin: string; -}; - -export type CompilationRequestOptions = { - userArguments: string; - compilerOptions: { - executorRequest?: boolean; - skipAsm?: boolean; - producePp?: PPOptions | null; - produceAst?: boolean; - produceGccDump?: { - opened: boolean; - pass?: GccDumpViewSelectedPass; - treeDump?: boolean; - rtlDump?: boolean; - ipaDump?: boolean; - dumpFlags: any; - }; - produceOptInfo?: boolean; - produceCfg?: boolean; - produceGnatDebugTree?: boolean; - produceGnatDebug?: boolean; - produceIr?: LLVMIrBackendOptions | null; - produceLLVMOptPipeline?: LLVMOptPipelineBackendOptions | null; - produceDevice?: boolean; - produceRustMir?: boolean; - produceRustMacroExp?: boolean; - produceRustHir?: boolean; - produceHaskellCore?: boolean; - produceHaskellStg?: boolean; - produceHaskellCmm?: boolean; - cmakeArgs?: string; - customOutputFilename?: string; - overrides?: ConfiguredOverrides; - }; - executeParameters: { - args: string; - stdin: string; - }; - filters: Record<string, boolean>; - tools: ActiveTools[]; - libraries: CompileChildLibraries[]; -}; - -export type CompilationRequest = { - source: string; - compiler: string; - options: CompilationRequestOptions; - lang: string | null; - files: FiledataPair[]; - bypassCache?: boolean; -}; - export type LangInfo = { compiler: string; options: string; execArgs: string; execStdin: string; }; - -export type CompileChildLibraries = { - id: string; - version: string; -}; diff --git a/static/panes/compiler.ts b/static/panes/compiler.ts index b785c54e6..accc8a280 100644 --- a/static/panes/compiler.ts +++ b/static/panes/compiler.ts @@ -57,7 +57,14 @@ import {PPOptions} from './pp-view.interfaces.js'; import {CompilationStatus} from '../compiler-service.interfaces.js'; import {WidgetState} from '../widgets/libs-widget.interfaces.js'; import {LLVMOptPipelineBackendOptions} from '../../types/compilation/llvm-opt-pipeline-output.interfaces.js'; -import {CompilationResult, FiledataPair} from '../../types/compilation/compilation.interfaces.js'; +import { + ActiveTools, + BypassCache, + CompilationRequest, + CompilationRequestOptions, + CompilationResult, + FiledataPair, +} from '../../types/compilation/compilation.interfaces.js'; import {ResultLine} from '../../types/resultline/resultline.interfaces.js'; import * as utils from '../utils.js'; import {editor} from 'monaco-editor'; @@ -70,7 +77,6 @@ import {SourceAndFiles} from '../download-service.js'; import fileSaver = require('file-saver'); import {ICompilerShared} from '../compiler-shared.interfaces.js'; import {CompilerShared} from '../compiler-shared.js'; -import type {ActiveTools, CompilationRequest, CompilationRequestOptions} from './compiler-request.interfaces.js'; import {SentryCapture} from '../sentry.js'; import {LLVMIrBackendOptions} from '../compilation/ir.interfaces.js'; @@ -1241,7 +1247,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co options: options, lang: this.currentLangId, files: tree.multifileService.getFiles(), - bypassCache: false, + bypassCache: BypassCache.None, }; const fetches: Promise<void>[] = []; @@ -1268,7 +1274,7 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co const cmakeProject = tree.multifileService.isACMakeProject(); request.files.push(...moreFiles); - if (bypassCache) request.bypassCache = true; + if (bypassCache) request.bypassCache = BypassCache.Compilation; if (!this.compiler) { this.onCompileResponse(request, this.errorResult('<Please select a compiler>'), false); } else if (cmakeProject && request.source === '') { @@ -1293,9 +1299,9 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co options: options, lang: this.currentLangId, files: sourceAndFiles.files, - bypassCache: false, + bypassCache: BypassCache.None, }; - if (bypassCache) request.bypassCache = true; + if (bypassCache) request.bypassCache = BypassCache.Compilation; if (!this.compiler) { this.onCompileResponse(request, this.errorResult('<Please select a compiler>'), false); } else { @@ -2903,7 +2909,6 @@ export class Compiler extends MonacoPane<monaco.editor.IStandaloneCodeEditor, Co }); this.compileClearCache.on('click', () => { - this.compilerService.cache.clear(); this.compile(true); }); diff --git a/static/panes/device-view.ts b/static/panes/device-view.ts index 40cbd759b..5d1cd7f06 100644 --- a/static/panes/device-view.ts +++ b/static/panes/device-view.ts @@ -42,7 +42,6 @@ import {AssemblyDocumentationInstructionSet} from '../../types/features/assembly import {Alert} from '../widgets/alert'; import {Compiler} from './compiler'; - export class DeviceAsm extends MonacoPane<monaco.editor.IStandaloneCodeEditor, DeviceAsmState> { private decorations: Record<string, monaco.editor.IModelDeltaDecoration[]>; private prevDecorations: string[]; diff --git a/static/panes/executor.ts b/static/panes/executor.ts index b35ee9199..7f4170fd5 100644 --- a/static/panes/executor.ts +++ b/static/panes/executor.ts @@ -45,14 +45,20 @@ import {ExecutorState} from './executor.interfaces.js'; import {CompilerInfo} from '../../types/compiler.interfaces.js'; import {Language} from '../../types/languages.interfaces.js'; import {LanguageLibs} from '../options.interfaces.js'; -import {CompilationResult, FiledataPair} from '../../types/compilation/compilation.interfaces.js'; +import { + BypassCache, + CompilationRequest, + CompilationRequestOptions, + CompilationResult, + FiledataPair, +} from '../../types/compilation/compilation.interfaces.js'; import {ResultLine} from '../../types/resultline/resultline.interfaces.js'; import {CompilationStatus as CompilerServiceCompilationStatus} from '../compiler-service.interfaces.js'; import {CompilerPicker} from '../widgets/compiler-picker.js'; import {SourceAndFiles} from '../download-service.js'; import {ICompilerShared} from '../compiler-shared.interfaces.js'; import {CompilerShared} from '../compiler-shared.js'; -import {CompilationRequest, CompilationRequestOptions, LangInfo} from './compiler-request.interfaces.js'; +import {LangInfo} from './compiler-request.interfaces.js'; const languages = options.languages; @@ -93,7 +99,6 @@ export class Executor extends Pane<ExecutorState> { private compilerPicker: CompilerPicker; private currentLangId: string; private toggleWrapButton: Toggles; - private compileClearCache: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private outputContentRoot: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private executionStatusSection: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private compilerOutputSection: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; @@ -114,7 +119,8 @@ export class Executor extends Pane<ExecutorState> { private panelArgs: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private panelStdin: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private wrapTitle: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; - private triggerCompilationButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; + private rerunButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; + private compileClearCache: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private wrapButton: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private toggleCompilation: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; private toggleArgs: JQuery<HTMLElementTagNameMap[keyof HTMLElementTagNameMap]>; @@ -260,7 +266,7 @@ export class Executor extends Pane<ExecutorState> { return {stdout: [], timedOut: false, code: -1, stderr: message}; } - compile(bypassCache?: boolean): void { + compile(bypassCache?: BypassCache): void { if (this.deferCompiles) { this.needsCompile = true; return; @@ -297,7 +303,7 @@ export class Executor extends Pane<ExecutorState> { } } - compileFromEditorSource(options: CompilationRequestOptions, bypassCache?: boolean): void { + compileFromEditorSource(options: CompilationRequestOptions, bypassCache?: BypassCache): void { if (!this.compiler?.supportsExecute) { this.alertSystem.notify('This compiler (' + this.compiler?.name + ') does not support execution', { group: 'execution', @@ -312,7 +318,7 @@ export class Executor extends Pane<ExecutorState> { lang: this.currentLangId, files: sourceAndFiles.files, }; - if (bypassCache) request.bypassCache = true; + if (bypassCache) request.bypassCache = bypassCache; if (!this.compiler) { this.onCompileResponse(request, this.errorResult('<Please select a compiler>'), false); } else { @@ -321,7 +327,7 @@ export class Executor extends Pane<ExecutorState> { }); } - compileFromTree(options: CompilationRequestOptions, bypassCache?: boolean): void { + compileFromTree(options: CompilationRequestOptions, bypassCache?: BypassCache): void { const tree = this.hub.getTreeById(this.sourceTreeId ?? -1); if (!tree) { this.sourceTreeId = null; @@ -361,7 +367,7 @@ export class Executor extends Pane<ExecutorState> { const treeState = tree.currentState(); const cmakeProject = tree.multifileService.isACMakeProject(); - if (bypassCache) request.bypassCache = true; + if (bypassCache) request.bypassCache = bypassCache; if (!this.compiler) { this.onCompileResponse(request, this.errorResult('<Please select a compiler>'), false); } else if (cmakeProject && request.source === '') { @@ -740,7 +746,6 @@ export class Executor extends Pane<ExecutorState> { } initButtons(state: PaneState & ExecutorState): void { - this.compileClearCache = this.domRoot.find('.clear-cache'); this.outputContentRoot = this.domRoot.find('pre.content'); this.executionStatusSection = this.outputContentRoot.find('.execution-status'); this.compilerOutputSection = this.outputContentRoot.find('.compiler-output'); @@ -800,7 +805,8 @@ export class Executor extends Pane<ExecutorState> { this.wrapButton = this.domRoot.find('.wrap-lines'); this.wrapTitle = this.wrapButton.prop('title'); - this.triggerCompilationButton = this.bottomBar.find('.trigger-compilation'); + this.rerunButton = this.bottomBar.find('.rerun'); + this.compileClearCache = this.bottomBar.find('.clear-cache'); this.initToggleButtons(state); } @@ -928,11 +934,6 @@ export class Executor extends Pane<ExecutorState> { this.execStdinField.on('change', execStdinChange).on('keyup', execStdinChange); - this.compileClearCache.on('click', () => { - this.hub.compilerService.cache.clear(); - this.compile(true); - }); - // Dismiss the popover on escape. $(document).on('keyup.editable', e => { if (e.which === 27) { @@ -956,8 +957,12 @@ export class Executor extends Pane<ExecutorState> { this.togglePanel(this.toggleCompilerOut, this.compilerOutputSection); }); - this.triggerCompilationButton.on('click', () => { - this.compile(true); + this.rerunButton.on('click', () => { + this.compile(BypassCache.Execution); + }); + + this.compileClearCache.on('click', () => { + this.compile(BypassCache.Compilation); }); // Dismiss on any click that isn't either in the opening element, inside diff --git a/static/styles/explorer.scss b/static/styles/explorer.scss index 834a53fa7..95429c0b9 100644 --- a/static/styles/explorer.scss +++ b/static/styles/explorer.scss @@ -442,11 +442,6 @@ pre.content.wrap * { } } -.clear-cache { - position: absolute; - right: 0; -} - .change-language { line-height: 14px; min-width: 200px !important; diff --git a/static/styles/themes/dark-theme.scss b/static/styles/themes/dark-theme.scss index cb2e74a85..0e4b95513 100644 --- a/static/styles/themes/dark-theme.scss +++ b/static/styles/themes/dark-theme.scss @@ -530,12 +530,12 @@ textarea.form-control { -webkit-text-fill-color: transparent; background-image: linear-gradient( 60deg, - #ED4242 0, - #FFAA44 20%, - #FFF344 40%, - #43A35B 60%, - #447CFF 80%, - #9C44A9 100% + #ed4242 0, + #ffaa44 20%, + #fff344 40%, + #43a35b 60%, + #447cff 80%, + #9c44a9 100% ); } diff --git a/static/styles/themes/default-theme.scss b/static/styles/themes/default-theme.scss index 7c20fa750..30c6ac59e 100644 --- a/static/styles/themes/default-theme.scss +++ b/static/styles/themes/default-theme.scss @@ -120,12 +120,12 @@ a.navbar-brand img.logo.normal { -webkit-text-fill-color: transparent; background-image: linear-gradient( 60deg, - #ED4242 0, - #FFAA44 20%, - #FFF344 40%, - #43A35B 60%, - #447CFF 80%, - #9C44A9 100% + #ed4242 0, + #ffaa44 20%, + #fff344 40%, + #43a35b 60%, + #447cff 80%, + #9c44a9 100% ); } diff --git a/static/styles/themes/pink-theme.scss b/static/styles/themes/pink-theme.scss index bf9907597..0d2bd512e 100644 --- a/static/styles/themes/pink-theme.scss +++ b/static/styles/themes/pink-theme.scss @@ -551,12 +551,12 @@ textarea.form-control { -webkit-text-fill-color: transparent; background-image: linear-gradient( 60deg, - #ED4242 0, - #FFAA44 20%, - #FFF344 40%, - #43A35B 60%, - #447CFF 80%, - #9C44A9 100% + #ed4242 0, + #ffaa44 20%, + #fff344 40%, + #43a35b 60%, + #447cff 80%, + #9c44a9 100% ); } diff --git a/test/handlers/compile-tests.js b/test/handlers/compile-tests.js index d7ddba99e..cbcc2a759 100644 --- a/test/handlers/compile-tests.js +++ b/test/handlers/compile-tests.js @@ -27,6 +27,7 @@ import express from 'express'; import {CompileHandler, SetTestMode} from '../../lib/handlers/compile.js'; import {fakeProps} from '../../lib/properties.js'; +import {BypassCache} from '../../types/compilation/compilation.interfaces.js'; import {chai, makeCompilationEnvironment} from '../utils.js'; SetTestMode(); @@ -361,7 +362,7 @@ describe('Compiler tests', () => { res.should.be.json; res.body.input.options.should.deep.equals({ backendOptions: {}, - bypassCache: false, + bypassCache: BypassCache.None, executionParameters: { args: [], stdin: '', diff --git a/types/compilation/compilation.interfaces.ts b/types/compilation/compilation.interfaces.ts index 994c12820..e79c20eb0 100644 --- a/types/compilation/compilation.interfaces.ts +++ b/types/compilation/compilation.interfaces.ts @@ -24,12 +24,97 @@ import {BuildEnvDownloadInfo} from '../../lib/buildenvsetup/buildenv.interfaces.js'; import {IAsmParser} from '../../lib/parsers/asm-parser.interfaces.js'; +import type {GccDumpViewSelectedPass} from '../../static/panes/gccdump-view.interfaces.js'; +import type {PPOptions} from '../../static/panes/pp-view.interfaces.js'; import {CompilerInfo} from '../compiler.interfaces.js'; import {BasicExecutionResult} from '../execution/execution.interfaces.js'; +import {ParseFiltersAndOutputOptions} from '../features/filters.interfaces.js'; import {ResultLine} from '../resultline/resultline.interfaces.js'; import {Artifact, ToolResult} from '../tool.interfaces.js'; -import {LLVMOptPipelineOutput} from './llvm-opt-pipeline-output.interfaces.js'; +import {ConfiguredOverrides} from './compiler-overrides.interfaces.js'; +import {LLVMIrBackendOptions} from './ir.interfaces.js'; +import {LLVMOptPipelineBackendOptions, LLVMOptPipelineOutput} from './llvm-opt-pipeline-output.interfaces.js'; + +export type ActiveTools = { + id: number; + args: string[]; + stdin: string; +}; + +export type ExecutionParams = { + args: string[] | string; + stdin: string; +}; + +export type CompileChildLibraries = { + id: string; + version: string; +}; + +export type CompilationRequestOptions = { + userArguments: string; + compilerOptions: { + executorRequest?: boolean; + skipAsm?: boolean; + producePp?: PPOptions | null; + produceAst?: boolean; + produceGccDump?: { + opened: boolean; + pass?: GccDumpViewSelectedPass; + treeDump?: boolean; + rtlDump?: boolean; + ipaDump?: boolean; + dumpFlags: any; + }; + produceOptInfo?: boolean; + produceCfg?: boolean; + produceGnatDebugTree?: boolean; + produceGnatDebug?: boolean; + produceIr?: LLVMIrBackendOptions | null; + produceLLVMOptPipeline?: LLVMOptPipelineBackendOptions | null; + produceDevice?: boolean; + produceRustMir?: boolean; + produceRustMacroExp?: boolean; + produceRustHir?: boolean; + produceHaskellCore?: boolean; + produceHaskellStg?: boolean; + produceHaskellCmm?: boolean; + cmakeArgs?: string; + customOutputFilename?: string; + overrides?: ConfiguredOverrides; + }; + executeParameters: ExecutionParams; + filters: ParseFiltersAndOutputOptions; + tools: ActiveTools[]; + libraries: CompileChildLibraries[]; +}; + +// Carefully chosen for backwards compatibility +// Compilation will imply exec (this is important for backward compatibility, though there is a world in which it could +// be desirable to only bypass a compilation cache and not the execution pass) +export enum BypassCache { + None = 0, + Compilation = 1, + Execution = 2, +} + +export function bypassCompilationCache(value: BypassCache) { + return value === BypassCache.Compilation; +} + +export function bypassExecutionCache(value: BypassCache) { + return value === BypassCache.Compilation || value === BypassCache.Execution; +} + +export type CompilationRequest = { + source: string; + compiler: string; + options: CompilationRequestOptions; + lang: string | null; + files: FiledataPair[]; + bypassCache?: BypassCache; +}; export type CompilationResult = { code: number; diff --git a/types/compiler.interfaces.ts b/types/compiler.interfaces.ts index 9036e0ce9..e641f3529 100644 --- a/types/compiler.interfaces.ts +++ b/types/compiler.interfaces.ts @@ -22,6 +22,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +import {BypassCache} from './compilation/compilation.interfaces.js'; import {AllCompilerOverrideOptions} from './compilation/compiler-overrides.interfaces.js'; import {ICompilerArguments} from './compiler-arguments.interfaces.js'; import {Language, LanguageKey} from './languages.interfaces.js'; @@ -143,7 +144,7 @@ export interface ICompiler { possibleArguments: ICompilerArguments; lang: Language; compile(source, options, backendOptions, filters, bypassCache, tools, executionParameters, libraries, files); - cmake(files, key); + cmake(files, key, bypassCache: BypassCache); initialise(mtime: Date, clientOptions, isPrediscovered: boolean); getInfo(): CompilerInfo; } diff --git a/types/features/filters.interfaces.ts b/types/features/filters.interfaces.ts index 5323cb248..13179c544 100644 --- a/types/features/filters.interfaces.ts +++ b/types/features/filters.interfaces.ts @@ -27,24 +27,26 @@ // options" and "Compiler output filters" drop down menu in a compiler pane. // TODO(jeremy-rifkin): Change name to include "filters"? -export type CompilerOutputOptions = { +export type CompilerOutputOptions = Partial<{ binary: boolean; binaryObject: boolean; execute: boolean; demangle: boolean; intel: boolean; -}; +}>; export type preProcessLinesFunc = (lines: string[]) => string[]; -export type ParseFiltersAndOutputOptions = { - labels: boolean; - libraryCode: boolean; - directives: boolean; - commentOnly: boolean; - trim: boolean; - debugCalls?: boolean; - dontMaskFilenames?: boolean; - optOutput: boolean; - preProcessLines?: preProcessLinesFunc; - preProcessBinaryAsmLines?: preProcessLinesFunc; -} & CompilerOutputOptions; +export type ParseFiltersAndOutputOptions = Partial< + { + labels: boolean; + libraryCode: boolean; + directives: boolean; + commentOnly: boolean; + trim: boolean; + debugCalls?: boolean; + dontMaskFilenames?: boolean; + optOutput: boolean; + preProcessLines?: preProcessLinesFunc; + preProcessBinaryAsmLines?: preProcessLinesFunc; + } & CompilerOutputOptions +>; diff --git a/views/templates/panes/executor.pug b/views/templates/panes/executor.pug index 7abb7b582..b18b76307 100644 --- a/views/templates/panes/executor.pug +++ b/views/templates/panes/executor.pug @@ -24,8 +24,6 @@ button.btn.btn-sm.btn-light.toggle-compilerout.active(title="Compiler output" aria-label="Toggle showing compiler output") span.fas.fa-sign-out-alt span.hideable Compiler output - button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile") - span.fas.fa-redo .top-bar.btn-toolbar.bg-light.panel-compilation(role="toolbar") .btn-group.btn-group-sm(role="group" aria-label="Compiler picker") .input-group @@ -58,7 +56,9 @@ .compiler-output .execution-output .bottom-bar.bg-light - button.btn.btn-sm.btn-light.trigger-compilation(title="Trigger compilation") + button.btn.btn-sm.btn-light.rerun(title="Rerun") + span.fas.fa-circle-play + button.btn.btn-sm.btn-light.clear-cache(title="Clear cache & recompile") span.fas.fa-redo span.short-compiler-name button.btn.btn-sm.btn-light.fas.fa-info.full-compiler-name(data-trigger="click" style="cursor: pointer;" role="button") |