From 24a24257302b4dc00d6e6615b72c28dfc19967d8 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 14 Jan 2023 15:44:14 +0100 Subject: [PATCH] Add Julia (#4595) --- .github/labeler.yml | 3 + etc/config/julia.amazon.properties | 14 +++ etc/config/julia.defaults.properties | 6 ++ etc/scripts/julia_wrapper.jl | 121 +++++++++++++++++++++++ examples/julia/default.jl | 5 + lib/compilers/_all.js | 1 + lib/compilers/julia.ts | 141 +++++++++++++++++++++++++++ lib/languages.ts | 11 +++ types/languages.interfaces.ts | 1 + views/resources/logos/julia.svg | 8 ++ webpack.config.esm.js | 1 + 11 files changed, 312 insertions(+) create mode 100644 etc/config/julia.amazon.properties create mode 100644 etc/config/julia.defaults.properties create mode 100755 etc/scripts/julia_wrapper.jl create mode 100644 examples/julia/default.jl create mode 100644 lib/compilers/julia.ts create mode 100644 views/resources/logos/julia.svg diff --git a/.github/labeler.yml b/.github/labeler.yml index f7c548794ff..16f0d1f3d8f 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -101,6 +101,9 @@ lang-mlir: lang-java: - lib/compilers/java.js - etc/config/java.*.properties +lang-julia: + - lib/compilers/julia.js + - etc/config/julia.*.properties lang-kotlin: - lib/compilers/kotlin.js - etc/config/kotlin.*.properties diff --git a/etc/config/julia.amazon.properties b/etc/config/julia.amazon.properties new file mode 100644 index 00000000000..5bfb3990d9f --- /dev/null +++ b/etc/config/julia.amazon.properties @@ -0,0 +1,14 @@ +# Default settings for Julia +compilers=&julia +defaultCompiler=julia_1_8_5 +compilerType=julia + +group.julia.compilers=julia_1_8_5:julia_1_7_3 +group.julia.isSemVer=true +group.julia.baseName=Julia +compiler.julia_1_8_5.exe=/opt/compiler-explorer/julia-1.8.5/bin/julia +compiler.julia_1_8_5.semver=1.8.5 +compiler.julia_1_8_5.alias=julia_1_8_5 +compiler.julia_1_7_3.exe=/opt/compiler-explorer/julia-1.7.3/bin/julia +compiler.julia_1_7_3.semver=1.7.3 +compiler.julia_1_7_3.alias=julia_1_7_3 diff --git a/etc/config/julia.defaults.properties b/etc/config/julia.defaults.properties new file mode 100644 index 00000000000..b5c48852980 --- /dev/null +++ b/etc/config/julia.defaults.properties @@ -0,0 +1,6 @@ +# Default settings for Julia +compilers=julia +defaultCompiler=julia +compilerType=julia +interpreted=true +supportsBinary=false diff --git a/etc/scripts/julia_wrapper.jl b/etc/scripts/julia_wrapper.jl new file mode 100755 index 00000000000..09111f21026 --- /dev/null +++ b/etc/scripts/julia_wrapper.jl @@ -0,0 +1,121 @@ +doc = """Julia wrapper. + +Usage: + julia_wrapper.jl [--format=] [--debuginfo=] [--optimize] [--verbose] + julia_wrapper.jl --help + +Options: + -h --help Show this screen. + --format= Set output format (One of "lowered", "typed", "warntype", "llvm", "native") [default: native] + lowered + typed + warntype + llvm + native + --debuginfo= Controls amount of generated metadata (One of "default", "none") [default: default] + --optimize Sets whether "llvm" output should be optimized or not. + --verbose Prints some process info +""" + +using InteractiveUtils + +if length(ARGS) < 2 + println(doc) + exit(1) +end + +if length(ARGS) > 3 && ARGS[3] == "--help" + println(doc) +end + +input_file = popfirst!(ARGS) +output_path = popfirst!(ARGS) +format = "native" +debuginfo = :default +optimize = false +verbose = false + +for x in ARGS + if startswith(x, "--format=") + global format = x[10:end] + end + if startswith(x, "--debuginfo=") + if x[13:end] == "none" + global debuginfo = :none + end + end + if x == "--optimize" + global optimize = true + end + if x == "--verbose" + global verbose = true + end +end + +# Include user code into module +m = Module(:Godbolt) +Base.include(m, input_file) + +# Find functions and method specializations +m_methods = Any[] +for name in names(m, all=true) + local fun = getfield(m, name) + if fun isa Function + if verbose + println("Function: ", fun) + end + for me in methods(fun) + for s in me.specializations + if s != nothing + me_types = getindex(s.specTypes.parameters, 2:length(s.specTypes.parameters)) + push!(m_methods, (fun, me_types, me)) + if verbose + println(" Method types: ", me_types) + end + end + end + end + end +end + +# Open output file +open(output_path, "w") do io + # For all found methods + for (me_fun, me_types, me) in m_methods + io_buf = IOBuffer() # string buffer + if format == "typed" + ir, retval = InteractiveUtils.code_typed(me_fun, me_types, debuginfo=debuginfo)[1] + Base.IRShow.show_ir(io_buf, ir) + elseif format == "lowered" + cl = Base.code_lowered(me_fun, me_types, debuginfo=debuginfo) + print(io_buf, cl) + elseif format == "llvm" + InteractiveUtils.code_llvm(io_buf, me_fun, me_types, optimize=optimize, debuginfo=debuginfo) + elseif format == "native" + InteractiveUtils.code_native(io_buf, me_fun, me_types, debuginfo=debuginfo) + elseif format == "warntype" + InteractiveUtils.code_warntype(io_buf, me_fun, me_types, debuginfo=debuginfo) + end + code = String(take!(io_buf)) + line_num = count("\n",code) + # Print first line: <[source code line] [number of output lines] [function name] [method types]> + write(io, "<") + print(io, me.line) + print(io, " ") + print(io, line_num) + print(io, " ") + print(io, me_fun) + write(io, " ") + for i in 1:length(me_types) + print(io, me_types[i]) + if i < length(me_types) + write(io, ", ") + end + end + write(io, ">\n") + # Print code for this method + write(io, code) + write(io, "\n") + end +end +exit(0) diff --git a/examples/julia/default.jl b/examples/julia/default.jl new file mode 100644 index 00000000000..ba349415e90 --- /dev/null +++ b/examples/julia/default.jl @@ -0,0 +1,5 @@ +function square(x) + return x * x +end + +precompile(square, (Int32,)) diff --git a/lib/compilers/_all.js b/lib/compilers/_all.js index b2d1f920dbe..1f7d7012d41 100644 --- a/lib/compilers/_all.js +++ b/lib/compilers/_all.js @@ -64,6 +64,7 @@ export {HookCompiler} from './hook'; export {ISPCCompiler} from './ispc'; export {JaktCompiler} from './jakt'; export {JavaCompiler} from './java'; +export {JuliaCompiler} from './julia'; export {KotlinCompiler} from './kotlin'; export {LDCCompiler} from './ldc'; export {LLCCompiler} from './llc'; diff --git a/lib/compilers/julia.ts b/lib/compilers/julia.ts new file mode 100644 index 00000000000..38d99e22802 --- /dev/null +++ b/lib/compilers/julia.ts @@ -0,0 +1,141 @@ +// Copyright (c) 2018, 2023, Elliot Saba & 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 e from 'express'; +import fs from 'fs'; +import path from 'path'; + +import {ParsedAsmResultLine} from '../../types/asmresult/asmresult.interfaces'; +import {CompilationResult, ExecutionOptions} from '../../types/compilation/compilation.interfaces'; +import {BaseCompiler} from '../base-compiler'; +import {logger} from '../logger'; +import * as utils from '../utils'; + +import {BaseParser} from './argument-parsers'; + +export class JuliaCompiler extends BaseCompiler { + private compilerWrapperPath: string; + + static get key() { + return 'julia'; + } + + constructor(info, env) { + super(info, env); + this.compiler.demangler = ''; + this.demanglerClass = null; + this.compilerWrapperPath = + this.compilerProps('compilerWrapper', '') || + utils.resolvePathFromAppRoot('etc', 'scripts', 'julia_wrapper.jl'); + } + + // No demangling for now + override postProcessAsm(result) { + return result; + } + + override getSharedLibraryPathsAsArguments() { + return []; + } + + override processAsm(result, filters, options) { + const lineRe = /^<(\d+) (\d+) ([^ ]+) ([^>]*)>$/; + const bytecodeLines = result.asm.split('\n'); + const bytecodeResult: ParsedAsmResultLine[] = []; + // Every method block starts with a introductory line + // <[source code line] [output line number] [function name] [method types]> + // Check for the starting line, add the method block, skip other lines + let i = 0; + while (i < bytecodeLines.length) { + const line = bytecodeLines[i]; + const match = line.match(lineRe); + + if (match) { + const source = parseInt(match[1]); + let linenum = parseInt(match[2]); + linenum = Math.min(linenum, bytecodeLines.length); + const funname = match[3]; + const types = match[4]; + let j = 0; + bytecodeResult.push({text: '<' + funname + ' ' + types + '>', source: {line: source, file: null}}); + while (j < linenum) { + bytecodeResult.push({text: bytecodeLines[i + 1 + j], source: {line: source, file: null}}); + j++; + } + bytecodeResult.push({text: '', source: {file: null}}); + i += linenum + 1; + continue; + } + i++; + } + return {asm: bytecodeResult}; + } + + override optionsForFilter(filters, outputFilename) { + return []; + } + + override getArgumentParser() { + return BaseParser; + } + + override fixExecuteParametersForInterpreting(executeParameters, outputFilename, key) { + super.fixExecuteParametersForInterpreting(executeParameters, outputFilename, key); + executeParameters.args.unshift('--'); + } + + override async runCompiler( + compiler: string, + options: string[], + inputFilename: string, + execOptions: ExecutionOptions, + ): Promise { + if (!execOptions) { + execOptions = this.getDefaultExecOptions(); + } + + const dirPath = path.dirname(inputFilename); + + if (!execOptions.customCwd) { + execOptions.customCwd = dirPath; + } + + // compiler wrapper, then input should be first argument, not last + const wrapperOptions = options.filter(opt => opt !== inputFilename); + + const juliaOptions = [this.compilerWrapperPath, '--']; + if (options.includes('-h') || options.includes('--help')) { + juliaOptions.push('--help'); + } else { + wrapperOptions.unshift(inputFilename, this.getOutputFilename(dirPath, this.outputFilebase)); + juliaOptions.push(...wrapperOptions); + } + + const execResult = await this.exec(compiler, juliaOptions, execOptions); + return { + compilationOptions: juliaOptions, + ...this.transformToCompilationResult(execResult, inputFilename), + }; + } +} diff --git a/lib/languages.ts b/lib/languages.ts index 61896f5811c..ad1517cfe9c 100644 --- a/lib/languages.ts +++ b/lib/languages.ts @@ -385,6 +385,17 @@ const definitions: Record = { previewFilter: null, monacoDisassembly: null, }, + julia: { + name: 'Julia', + monaco: 'julia', + extensions: ['.jl'], + alias: [], + logoUrl: 'julia.svg', + logoUrlDark: null, + formatter: null, + previewFilter: null, + monacoDisassembly: null, + }, kotlin: { name: 'Kotlin', monaco: 'kotlin', diff --git a/types/languages.interfaces.ts b/types/languages.interfaces.ts index a08a5de26dc..74615915379 100644 --- a/types/languages.interfaces.ts +++ b/types/languages.interfaces.ts @@ -53,6 +53,7 @@ export type LanguageKey = | 'ispc' | 'jakt' | 'java' + | 'julia' | 'kotlin' | 'llvm' | 'mlir' diff --git a/views/resources/logos/julia.svg b/views/resources/logos/julia.svg new file mode 100644 index 00000000000..1148f5c7f2f --- /dev/null +++ b/views/resources/logos/julia.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/webpack.config.esm.js b/webpack.config.esm.js index 68cd5a38c74..ca9cf32b422 100644 --- a/webpack.config.esm.js +++ b/webpack.config.esm.js @@ -67,6 +67,7 @@ const plugins = [ 'rust', 'swift', 'java', + 'julia', 'kotlin', 'scala', 'ruby',