diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..beffa7894 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,28 @@ +FROM rust:latest + +# Install build dependencies +RUN apt-get update && apt-get install -y \ + build-essential \ + pkg-config \ + gcc-aarch64-linux-gnu \ + gcc-riscv64-linux-gnu \ + gcc-arm-linux-gnueabihf \ + libc6-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +# Copy the source code +COPY . . + +# Build the ccc compiler in release mode +RUN cargo build --release + +# Create a working directory for files to compile +WORKDIR /workspace + +# Set the default binary to ccc (x86-64) +ENTRYPOINT ["/app/target/release/ccc"] + +# Default command: show usage +CMD ["--help"] diff --git a/ccc-docker.sh b/ccc-docker.sh new file mode 100755 index 000000000..bf820c6fb --- /dev/null +++ b/ccc-docker.sh @@ -0,0 +1,112 @@ +#!/bin/bash + +set -e + +# Parse arguments +if [ "$1" != "--run" ]; then + echo "Usage: $0 --run [ccc arguments...]" + echo "" + echo "Example:" + echo " $0 --run hello.c -o hello" + echo " $0 --run test.c" + exit 1 +fi + +shift # remove --run + +if [ -z "$1" ]; then + echo "Error: filename required" + echo "Usage: $0 --run [ccc arguments...]" + exit 1 +fi + +filename="$1" +shift # remove filename + +# Check if file exists +if [ ! -f "$filename" ]; then + echo "Error: File '$filename' not found" + exit 1 +fi + +# Get absolute path +abs_filename=$(cd "$(dirname "$filename")" && pwd)/$(basename "$filename") +work_dir=$(dirname "$abs_filename") +file_basename=$(basename "$filename") + +# Determine output filename +output_name="" +remaining_args=() +prev_arg="" +for arg in "$@"; do + if [ "$prev_arg" = "-o" ]; then + output_name="$arg" + else + remaining_args+=("$arg") + fi + prev_arg="$arg" +done + +# If no -o flag, use filename without extension +if [ -z "$output_name" ]; then + output_name="${file_basename%.c}" +fi + +# Get the directory where the script is located +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Build the Docker image if it doesn't exist +image_name="ccc-compiler" +if ! docker image inspect "$image_name" > /dev/null 2>&1; then + echo "Building Docker image: $image_name" + docker build -t "$image_name" "$script_dir" +fi + +# Build extra args string (shell-escaped) +extra_args="" +for arg in "${remaining_args[@]}"; do + extra_args+=" $(printf '%q' "$arg")" +done + +# Compile the file inside the container with dynamic include paths +echo "Compiling $filename with ccc in Docker..." +docker run --rm \ + -v "$work_dir:/workspace" \ + --entrypoint /bin/sh \ + -e OUTPUT_NAME="$output_name" \ + -e FILE_BASENAME="$file_basename" \ + -e EXTRA_ARGS="$extra_args" \ + "$image_name" \ + -lc ' +set -e +ARCH=$(gcc -dumpmachine) +INCDIR=$(gcc -print-file-name=include) +if echo "$ARCH" | grep -q "aarch64"; then + CCC=/app/target/release/ccc-arm +else + CCC=/app/target/release/ccc +fi + +"$CCC" \ + -isystem /usr/include \ + -isystem "/usr/include/${ARCH}" \ + -isystem "${INCDIR}" \ + -L "/usr/lib/${ARCH}" \ + -L "/lib/${ARCH}" \ + -lc \ + -o "/workspace/${OUTPUT_NAME}" \ + "/workspace/${FILE_BASENAME}" ${EXTRA_ARGS} +' + +echo "" +echo "Running compiled binary..." +docker run --rm \ + -v "$work_dir:/workspace" \ + --entrypoint /bin/sh \ + -e OUTPUT_NAME="$output_name" \ + "$image_name" \ + -lc ' +ARCH=$(gcc -dumpmachine) +export LD_LIBRARY_PATH="/lib/${ARCH}:/usr/lib/${ARCH}:${LD_LIBRARY_PATH}" +exec "/workspace/${OUTPUT_NAME}" +' diff --git a/src/backend/arm/assembler/encoder/load_store.rs b/src/backend/arm/assembler/encoder/load_store.rs index c60b26482..aba82cd57 100644 --- a/src/backend/arm/assembler/encoder/load_store.rs +++ b/src/backend/arm/assembler/encoder/load_store.rs @@ -63,7 +63,7 @@ pub(crate) fn encode_ldr_str(operands: &[Operand], is_load: bool, size: u32, is_ // Check if offset is aligned and fits in 12-bit unsigned field let abs_offset = *offset as u64; let align = 1u64 << shift; - if *offset >= 0 && abs_offset.is_multiple_of(align) { + if *offset >= 0 && crate::common::is_multiple_of::is_multiple_of(abs_offset, align) { let imm12 = (abs_offset / align) as u32; if imm12 < 4096 { // Unsigned offset form: size 111 V 01 opc imm12 Rn Rt @@ -321,7 +321,7 @@ pub(crate) fn encode_ldrsw(operands: &[Operand]) -> Result // LDRSW: size=10 111 V=0 01 opc=10 -> unsigned offset // Actually: 10 111 0 01 10 imm12 Rn Rt let abs_offset = *offset as u64; - if *offset >= 0 && abs_offset.is_multiple_of(4) { + if *offset >= 0 && crate::common::is_multiple_of::is_multiple_of(abs_offset, 4) { let imm12 = (abs_offset / 4) as u32; if imm12 < 4096 { let word = ((0b10 << 30) | (0b111 << 27)) | (0b01 << 24) | (0b10 << 22) @@ -393,7 +393,7 @@ pub(crate) fn encode_ldrs(operands: &[Operand], size: u32) -> Result= 0 && abs_offset.is_multiple_of(align) { + if *offset >= 0 && crate::common::is_multiple_of::is_multiple_of(abs_offset, align) { let imm12 = (abs_offset / align) as u32; if imm12 < 4096 { let word = ((size << 30) | (0b111 << 27)) | (0b01 << 24) | (opc << 22) diff --git a/src/backend/arm/codegen/emit.rs b/src/backend/arm/codegen/emit.rs index 9610e0742..9c06182f0 100644 --- a/src/backend/arm/codegen/emit.rs +++ b/src/backend/arm/codegen/emit.rs @@ -1427,7 +1427,7 @@ impl ArmCodegen { for (i, _) in args.iter().enumerate() { match arg_classes[i] { CallArgClass::I128RegPair { .. } => { - if !int_reg_idx.is_multiple_of(2) { int_reg_idx += 1; } + if !crate::common::is_multiple_of::is_multiple_of(int_reg_idx, 2) { int_reg_idx += 1; } int_reg_idx += 2; } CallArgClass::StructByValReg { size, .. } => { diff --git a/src/backend/call_abi.rs b/src/backend/call_abi.rs index 4cd8a531a..6f9c61e93 100644 --- a/src/backend/call_abi.rs +++ b/src/backend/call_abi.rs @@ -476,7 +476,7 @@ fn classify_args_core( // Note: ARM AAPCS64 does NOT require even-aligned pairs for composites. if regs_needed == 2 && config.align_struct_pairs { let struct_align = info.struct_align.unwrap_or(slot_size); - if struct_align > slot_size && !int_idx.is_multiple_of(2) { + if struct_align > slot_size && (int_idx % 2 == 1) { int_idx += 1; // skip to even register } } @@ -503,7 +503,7 @@ fn classify_args_core( result.push(CoreArgClass::LargeStructStack { size }); } } else if info.is_i128 { - if config.align_i128_pairs && !int_idx.is_multiple_of(2) { + if config.align_i128_pairs && !crate::common::is_multiple_of::is_multiple_of(int_idx, 2) { int_idx += 1; } if int_idx + 1 < config.max_int_regs { @@ -522,7 +522,7 @@ fn classify_args_core( result.push(CoreArgClass::F128Stack); } } else if config.f128_in_gp_pairs { - if config.align_i128_pairs && !int_idx.is_multiple_of(2) { + if config.align_i128_pairs && !crate::common::is_multiple_of::is_multiple_of(int_idx, 2) { int_idx += 1; } if int_idx + 1 < config.max_int_regs { diff --git a/src/backend/riscv/linker/emit_shared.rs b/src/backend/riscv/linker/emit_shared.rs index 824fd73fd..6d58083ca 100644 --- a/src/backend/riscv/linker/emit_shared.rs +++ b/src/backend/riscv/linker/emit_shared.rs @@ -966,7 +966,7 @@ pub fn emit_shared_library( 0, shstrtab_file_offset, shstrtab_data.len() as u64, 0, 0, 1, 0)); // Write section headers - while !elf.len().is_multiple_of(8) { elf.push(0); } + while !crate::common::is_multiple_of::is_multiple_of(elf.len(), 8) { elf.push(0); } let shdr_offset = elf.len() as u64; let shdr_count = section_headers.len(); diff --git a/src/backend/x86/codegen/calls.rs b/src/backend/x86/codegen/calls.rs index b2c7d4954..87fc4e265 100644 --- a/src/backend/x86/codegen/calls.rs +++ b/src/backend/x86/codegen/calls.rs @@ -28,7 +28,7 @@ impl X86Codegen { pub(super) fn emit_call_stack_args_impl(&mut self, args: &[Operand], arg_classes: &[CallArgClass], _arg_types: &[IrType], stack_arg_space: usize, _fptr_spill: usize, _f128_temp_space: usize) -> i64 { - let need_align_pad = !stack_arg_space.is_multiple_of(16); + let need_align_pad = !crate::common::is_multiple_of::is_multiple_of(stack_arg_space, 16); if need_align_pad { self.state.emit(" subq $8, %rsp"); } @@ -265,7 +265,7 @@ impl X86Codegen { } pub(super) fn emit_call_cleanup_impl(&mut self, stack_arg_space: usize, _f128_temp_space: usize, _indirect: bool) { - let need_align_pad = !stack_arg_space.is_multiple_of(16); + let need_align_pad = !crate::common::is_multiple_of::is_multiple_of(stack_arg_space, 16); let total_cleanup = stack_arg_space + if need_align_pad { 8 } else { 0 }; if total_cleanup > 0 { self.state.out.emit_instr_imm_reg(" addq", total_cleanup as i64, "rsp"); diff --git a/src/backend/x86/linker/emit_exec.rs b/src/backend/x86/linker/emit_exec.rs index c0c077815..7e816b986 100644 --- a/src/backend/x86/linker/emit_exec.rs +++ b/src/backend/x86/linker/emit_exec.rs @@ -1080,12 +1080,12 @@ pub(super) fn emit_executable( sh_count += 1; // Align and append .shstrtab data - while !out.len().is_multiple_of(8) { out.push(0); } + while !crate::common::is_multiple_of::is_multiple_of(out.len(), 8) { out.push(0); } let shstrtab_data_offset = out.len() as u64; out.extend_from_slice(&shstrtab); // Align section header table to 8 bytes - while !out.len().is_multiple_of(8) { out.push(0); } + while !crate::common::is_multiple_of::is_multiple_of(out.len(), 8) { out.push(0); } let shdr_offset = out.len() as u64; // Write section headers diff --git a/src/backend/x86/linker/emit_shared.rs b/src/backend/x86/linker/emit_shared.rs index a4c4d30ef..5fcf6a226 100644 --- a/src/backend/x86/linker/emit_shared.rs +++ b/src/backend/x86/linker/emit_shared.rs @@ -1085,12 +1085,12 @@ pub(super) fn emit_shared_library( sh_count += 1; // Align and append .shstrtab data - while !out.len().is_multiple_of(8) { out.push(0); } + while !crate::common::is_multiple_of::is_multiple_of(out.len(), 8) { out.push(0); } let shstrtab_data_offset = out.len() as u64; out.extend_from_slice(&shstrtab); // Align section header table to 8 bytes - while !out.len().is_multiple_of(8) { out.push(0); } + while !crate::common::is_multiple_of::is_multiple_of(out.len(), 8) { out.push(0); } let shdr_offset = out.len() as u64; // Write section headers diff --git a/src/common/is_multiple_of.rs b/src/common/is_multiple_of.rs new file mode 100644 index 000000000..acef39efc --- /dev/null +++ b/src/common/is_multiple_of.rs @@ -0,0 +1,3 @@ +pub fn is_multiple_of + PartialEq + From + Copy>(value: T, divisor: T) -> bool { + divisor != T::from(0) && value % divisor == T::from(0) +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 93fef3328..98c243e8a 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -10,3 +10,4 @@ pub(crate) mod symbol_table; pub(crate) mod temp_files; pub(crate) mod type_builder; pub(crate) mod types; +pub(crate) mod is_multiple_of;