Skip to content

Commit

Permalink
[llvm] Improved error handling if build_cmd fails.
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisCummins committed Feb 17, 2022
1 parent e2c54a1 commit 3e79624
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 44 deletions.
41 changes: 38 additions & 3 deletions compiler_gym/envs/llvm/service/Cost.cc
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ Status getTextSizeInBytes(const fs::path& file, int64_t* value) {
return Status::OK;
}

#define TEXT_SIZE_RETURN_IF_ERROR(expr) \
while (1) { \
const auto status = expr; \
if (!status.ok()) { \
return Status(StatusCode::INVALID_ARGUMENT, \
fmt::format("Failed to compute .text size cost: {}", status.error_message())); \
} \
break; \
}

Status getTextSizeInBytes(llvm::Module& module, int64_t* value, const fs::path& workingDirectory,
const util::LocalShellCommand& buildCommand) {
if (buildCommand.outfiles().size() != 1) {
Expand All @@ -122,20 +132,45 @@ Status getTextSizeInBytes(llvm::Module& module, int64_t* value, const fs::path&
// Write the bitcode to the expected place.
RETURN_IF_ERROR(writeBitcodeFile(module, "out.bc"));

RETURN_IF_ERROR(buildCommand.checkInfiles());
RETURN_IF_ERROR(buildCommand.checkCall());
RETURN_IF_ERROR(buildCommand.checkOutfiles());
TEXT_SIZE_RETURN_IF_ERROR(buildCommand.checkInfiles());
TEXT_SIZE_RETURN_IF_ERROR(buildCommand.checkCall());
TEXT_SIZE_RETURN_IF_ERROR(buildCommand.checkOutfiles());

return getTextSizeInBytes(outfile, value);
}

#undef TEXT_SIZE_RETURN_IF_ERROR

util::LocalShellCommand getBuildCommand(const BenchmarkDynamicConfig& dynamicConfig,
bool compile_only) {
// Append the '-c' flag to compile-only jobs.
if (compile_only) {
Command newCommand;
newCommand.CopyFrom(dynamicConfig.buildCommand().proto());

// Determine if the compilation specifies an output file using the `-o
// <file>` flag. If not, then add one so that when we append the `-c` flag
// we still know where the generated object file will go.
bool outfileSpecified = false;
for (int i = 0; i < newCommand.argument_size() - 1; ++i) {
if (newCommand.argument(i) == "-o") {
outfileSpecified = true;
break;
}
}
if (!outfileSpecified) {
newCommand.add_argument("-o");
if (newCommand.outfile_size() < 1) {
newCommand.add_argument("a.out");
newCommand.add_outfile("a.out");
} else {
const auto& outfile = newCommand.outfile(0);
newCommand.add_argument(outfile);
}
}

newCommand.add_argument("-c");

return util::LocalShellCommand(newCommand);
}
return dynamicConfig.buildCommand();
Expand Down
40 changes: 39 additions & 1 deletion tests/llvm/custom_benchmarks_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import gym
import pytest

from compiler_gym.datasets import Benchmark
from compiler_gym.datasets import Benchmark, BenchmarkInitError
from compiler_gym.envs import LlvmEnv, llvm
from compiler_gym.service.proto import Benchmark as BenchmarkProto
from compiler_gym.service.proto import File
Expand Down Expand Up @@ -311,5 +311,43 @@ def test_get_compiler_includes_output_parse_failure():
os.environ["CXX"] = old_cxx


def test_benchmark_with_invalid_build_cmd(env: LlvmEnv, tmpdir):
"""Test that BenchmarkInitError raised if the build command fails."""
with open(tmpdir / "program.c", "w") as f:
f.write(
"""
#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello\\n");
for (int i = 0; i < 10; ++i) {
argc += 2;
}
return argc - argc;
}
"""
)

benchmark = env.make_benchmark(Path(tmpdir) / "program.c")

benchmark.proto.dynamic_config.build_cmd.argument.extend(
["$CC", "$IN", "-invalid-cc-argument"]
)
benchmark.proto.dynamic_config.build_cmd.outfile.extend(["a.out"])
benchmark.proto.dynamic_config.build_cmd.timeout_seconds = 10

benchmark.proto.dynamic_config.run_cmd.argument.extend(["./a.out"])
benchmark.proto.dynamic_config.run_cmd.timeout_seconds = 10

with pytest.raises(
BenchmarkInitError,
match=(
r"Command '\$CC \$IN -invalid-cc-argument .+' failed with exit code "
r"1: clang: error: unknown argument: '-invalid-cc-argument'"
),
):
env.reset(benchmark=benchmark)


if __name__ == "__main__":
main()
40 changes: 0 additions & 40 deletions tests/llvm/runtime_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from flaky import flaky

from compiler_gym.envs.llvm import LlvmEnv
from compiler_gym.service.connection import ServiceError
from tests.test_main import main

pytest_plugins = ["tests.pytest_plugins.llvm"]
Expand Down Expand Up @@ -97,45 +96,6 @@ def test_custom_benchmark_runtimes_differ(env: LlvmEnv, tmpdir):
assert not np.all(runtimes_a == runtimes_b)


def test_failing_build_cmd(env: LlvmEnv, tmpdir):
"""Test that Runtime observation raises an error if build command fails."""
with open(tmpdir / "program.c", "w") as f:
f.write(
"""
#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello\\n");
for (int i = 0; i < 10; ++i) {
argc += 2;
}
return argc - argc;
}
"""
)

benchmark = env.make_benchmark(Path(tmpdir) / "program.c")

benchmark.proto.dynamic_config.build_cmd.argument.extend(
["$CC", "$IN", "-invalid-cc-argument"]
)
benchmark.proto.dynamic_config.build_cmd.outfile.extend(["a.out"])
benchmark.proto.dynamic_config.build_cmd.timeout_seconds = 10

benchmark.proto.dynamic_config.run_cmd.argument.extend(["./a.out"])
benchmark.proto.dynamic_config.run_cmd.timeout_seconds = 10

env.reset(benchmark=benchmark)
with pytest.raises(
ServiceError,
match=(
r"Command '\$CC \$IN -invalid-cc-argument' failed with exit code 1: "
r"clang: error: unknown argument: '-invalid-cc-argument'"
),
):
env.observation.Runtime()


def test_invalid_runtime_count(env: LlvmEnv):
env.reset()
with pytest.raises(
Expand Down

0 comments on commit 3e79624

Please sign in to comment.