From 8e39e3e368cc9ae86a23e27319824a7c9488c841 Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Fri, 21 Jun 2024 14:35:27 +0800 Subject: [PATCH] mix-object: add functions bisect --- src/mix-object.ts | 223 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 6 deletions(-) diff --git a/src/mix-object.ts b/src/mix-object.ts index 74719da..6c7edd7 100644 --- a/src/mix-object.ts +++ b/src/mix-object.ts @@ -7,14 +7,18 @@ */ import { binSearch } from "./algorithm"; -import { pop2, SPEC, Bench, mkBench, mkSPEC } from "./spec"; +import { pop2, SPEC, Bench, mkBench, mkSPEC, defaultSPEC, watchSPEC, parseSPECCSVResultsTable } from "./spec"; import path from "path"; -import { FLANG, PREFIX, SW_AUTOVEC, SYSROOT_PREFIX } from "./environment"; -import { generalCommand, linkerCommand } from "./commands/compiler"; +import { FLANG, LLVM_EXTRACT, PREFIX, SW_AUTOVEC, SYSROOT_PREFIX, projectRoot } from "./environment"; +import { extractCommand, generalCommand, linkerCommand } from "./commands/compiler"; import { runcpuOptions } from "./commands/spec"; import { checkedSpawnSync, promisifySpawn } from "./cli"; import { systemdRunOptions } from "./commands/systemd"; import { spawn } from "child_process"; +import { functionList } from "./llvm-parser"; +import assert from "assert"; +import fs from "fs/promises"; +import crypto from "crypto"; export interface Linkable { link: (objectPath: (objectNames: string[]) => string[]) => Promise; @@ -194,10 +198,10 @@ async function bisect( })); }; -(async () => { +const buildPathObj = (buildBase: string) => (build: string) => (objname: string) => path.join(buildBase, build, objname); +const buildDirs = ["build_vector", "build_scalar_vanilla"]; - const buildPathObj = (buildBase: string) => (build: string) => (objname: string) => path.join(buildBase, build, objname); - const buildDirs = ["build_vector", "build_scalar_vanilla"]; +async function verifyGridFunction() { const specs = [ "spec2017", @@ -268,4 +272,211 @@ async function bisect( })(specs[0]); +}; + +(async () => { + // It is known that object index == 207 is also wrong for pop2. + + const spec = defaultSPEC; + + const troubleName = pop2.objectNames[207].replace(/\.o$/, ""); + + const pop2bench = mkPop2Bench(spec); + + const local = path.resolve(projectRoot, "local"); + + // Maps from name of function -> path to object file. + const localSuffix = (suffix: string) => (fn: string) => path.resolve(local, `${fn}-${suffix}.o`); + const vectorObject = localSuffix("vector"); + const scalarObject = localSuffix("scalar"); + + const build = path.resolve(spec.buildpath(pop2), "build_vector_test"); + + // Compile that object to "ll" + async function compileIR() { + const llvmIR = spawn(FLANG, ([ + ...generalCommand({ + outputKind: "assembly", + output: "-", + sysroot: SYSROOT_PREFIX, + }), + "-emit-llvm", + "-O2", + "-msimd", + "-Mbyteswapio", + "-fno-discard-value-names", + path.resolve(build, `${troubleName}.f90`), + ]), { stdio: ['inherit', 'pipe', 'inherit'], cwd: build }); + + const chunks: Buffer[] = []; + + llvmIR.stdout.on("data", chunk => { chunks.push(Buffer.from(chunk)); }); + + const llvmIRResult = await promisifySpawn(llvmIR); + llvmIRResult.stdout; + + return Buffer.concat(chunks).toString("utf-8"); + } + + + const objFlags = [ + ...generalCommand({ + outputKind: "object", + }), + "-x", "ir", + // Read from stdin. + "-", + ]; + + const IR = await compileIR(); + + // First prepare a functions dir that would be suitable for replacing it's ".o" + async function prepareDir(IR: string, vectorObject: (fn: string) => string, scalarObject: (fn: string) => string) { + // Parse the IR with all function names + + const fnList = functionList(IR); + + // For each function, extract it. + await Promise.all(fnList.map(fn => { + const proc = spawn(LLVM_EXTRACT, [ + ...extractCommand({ + func: [fn], + asm: true, + input: "-", + output: "-", + }), + ]); + + // Write IR and close stdin + proc.stdin.write(IR); + proc.stdin.end(); + + const compileVector = spawn(FLANG, [ + ...objFlags, + ...generalCommand({ output: vectorObject(fn), }), + "-msimd", + ]); + + const compileScalar = spawn(FLANG, [ + ...objFlags, + ...generalCommand({ output: scalarObject(fn) }), + ]); + + proc.stdout.pipe(compileVector.stdin); + proc.stdout.pipe(compileScalar.stdin); + + return Promise.all([proc, compileScalar, compileScalar].map(promisifySpawn)); + })); + + return fnList; + } + + const fnList = await prepareDir(IR, vectorObject, scalarObject); + + // Also extract global names. + const globalObj = path.resolve(local, "207-global.o"); + await (async () => { + const proc = spawn(LLVM_EXTRACT, [ + ...extractCommand({ + asm: true, + input: "-", + output: "-", + }), + // Extract all global variables. + "-rglob", + ".*", + ], { stdio: ['pipe', 'pipe', 'inherit'] }); + + // Write IR and close stdin + proc.stdin.write(IR); + proc.stdin.end(); + + const compileScalar = spawn(FLANG, [ + ...objFlags, + ...generalCommand({ output: globalObj }), + ], { stdio: ['pipe', 'inherit', 'inherit'] }); + + proc.stdout.pipe(compileScalar.stdin); + + return Promise.all([proc, compileScalar].map(promisifySpawn)); + })(); + + // Bisect with replacing functions. + + const [vector, scalar] = buildDirs.map(dir => buildPathObj(spec.buildpath(pop2))(dir)); + + // Run many SPEC2017, with different "outputRoot" + for (const fn of fnList) { + console.log(`testing function ${fn}`); + // For each function, try use a scalar version of that func + // Others are vector version. + const linkingList = fnList.map(name => name == fn ? scalarObject(name) : vectorObject(name)); + + // Link together with other objects. + await pop2bench.link(objectNames => objectNames.flatMap((objectName, index) => { + if (index == 207) { + return [...linkingList, globalObj]; + } + if (objectName == "grid.fppized.o") { + return scalar(objectName); + } + return vector(objectName); + })); + + const unitName = `pop2-${fn}-${crypto.randomUUID()}`; + // Spawn SPEC process. + const runcpu = spawn("systemd-run", [ + ...systemdRunOptions({ + scope: true, + user: true, + unit: unitName, + }), + "runcpu", + ...runcpuOptions({ + benchmarks: [pop2.name], + buildType: "nobuild", + config: "clang-O2", + workload: "ref", + setprocgroup: true, + }) + ], { env: spec.getEnvironment() }); + + let OK = false; + + setTimeout(() => { + if (!OK) { + console.log("timeout, kill the process."); + checkedSpawnSync( + "systemctl", [ + "--user", + "stop", + `${unitName}.scope`, + ], { stdio: "inherit" } + ); + } + }, 10 * 60 * 1000); // 10min + + + runcpu.stdout.pipe(process.stdout); + runcpu.stderr.pipe(process.stderr); + + const result = await watchSPEC(runcpu); + + OK = true; + console.log(result); + + + if ("CSV" in result.outputFiles) { + assert(result.outputFiles.CSV.length > 0); + const Results = parseSPECCSVResultsTable((await fs.readFile(result.outputFiles.CSV[0])).toString("utf-8")); + let perlbenchResult; + if (perlbenchResult = Results[`${pop2.num}-${pop2.name}`]) { + if (perlbenchResult.baseStatus !== "S") { + console.log("Error!"); + } + } + } else { + console.log("SPEC does not produce CSV output!"); + } + } })();