From 0c33a58da02148a104bc01d3fdb44be45f03fbe8 Mon Sep 17 00:00:00 2001 From: olemorud Date: Tue, 6 Jun 2023 11:32:12 +0200 Subject: [PATCH] Test pseudo-namespaces in C In C you can create pseudo-namespaces by abusing structs of function pointers, making function calls look like `module.func(x)` instead of `func(x)`. A lot of people online spout that this creates overhead, but this should show that clang>=15.0.7 will still inline such function calls. GCC 11.3.1 is unable to inline the function, but will still call the function directly. --- CMakeLists.txt | 20 ++++++++++++++++++++ Makefile | 29 +++++++++++++++++++++++++++++ build.sh | 21 +++++++++++++++++++++ dump.asm | 10 ++++++++++ main.c | 22 ++++++++++++++++++++++ module.c | 12 ++++++++++++ module.h | 8 ++++++++ 7 files changed, 122 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Makefile create mode 100755 build.sh create mode 100644 dump.asm create mode 100644 main.c create mode 100644 module.c create mode 100644 module.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5f75245 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,20 @@ + +cmake_minimum_required(VERSION 3.13) + +project(Namespaces) + +add_executable(main + module.c + main.c) + +target_compile_options(main + PRIVATE + -Werror -Wall -Wextra -O3 -g -std=c2x -flto) + +target_link_options(main + PRIVATE + -flto -Wl,-O2) + +target_compile_definitions(main + PRIVATE + -DNAMESPACES) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e0addf7 --- /dev/null +++ b/Makefile @@ -0,0 +1,29 @@ + +CC ?= clang + +# compiler specific flags +CFLAGS.gcc := +CFLAGS.clang := + +# general flags +CFLAGS := \ + -Wall -Wextra -Werror -Wpedantic \ + -O3 -g -flto \ + -std=c2x \ + ${CFLAGS.${CC}} \ + -DNAMESPACES \ + +LDFLAGS := \ + -flto \ + -Wl,-O3 + +dump.asm : test + gdb $< -batch -ex 'disassemble main' > $@ + (gdb $< -batch -ex 'disassemble succ' >> $@) || true + +test : main.o module.o + $(CC) $(CFLAGS) -o $@ $^ + +%.o : %.c + $(CC) $(CFLAGS) -o $@ -c $^ + diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..7017255 --- /dev/null +++ b/build.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +requirements=("cmake" "gdb") + +for cmd in $requirements; do + if ! command -v gdb; then + echo "gdb not found" + exit 1 + fi +done + +mkdir -p build +(cd build + rm * -rf + CC=clang cmake .. + make -j2 +) + +gdb ./build/main -batch -ex 'disassemble main' > dump.asm +gdb ./build/main -batch -ex 'disassemble succ' 1>> dump.asm 2>/dev/null || true + diff --git a/dump.asm b/dump.asm new file mode 100644 index 0000000..7b43e0a --- /dev/null +++ b/dump.asm @@ -0,0 +1,10 @@ +Dump of assembler code for function main: + 0x0000000000401130 <+0>: push %rax + 0x0000000000401131 <+1>: mov $0x402010,%edi + 0x0000000000401136 <+6>: mov $0x6,%esi + 0x000000000040113b <+11>: xor %eax,%eax + 0x000000000040113d <+13>: call 0x401030 + 0x0000000000401142 <+18>: xor %eax,%eax + 0x0000000000401144 <+20>: pop %rcx + 0x0000000000401145 <+21>: ret +End of assembler dump. diff --git a/main.c b/main.c new file mode 100644 index 0000000..53603a4 --- /dev/null +++ b/main.c @@ -0,0 +1,22 @@ + +#include + +#include "module.h" + +int main() +{ + int x = 4; + +#ifdef NAMESPACES +#pragma message "namespaces enabled" + x = module.succ(x); + x = module.succ(x); +#else + x = succ(x); + x = succ(x); +#endif + + printf("%d\n", x); + + return 0; +} diff --git a/module.c b/module.c new file mode 100644 index 0000000..a66f545 --- /dev/null +++ b/module.c @@ -0,0 +1,12 @@ + +#include "module.h" + +inline int succ(int x) +{ + return x+1; +} + +struct exports module = { + .succ = succ, +}; + diff --git a/module.h b/module.h new file mode 100644 index 0000000..0acef84 --- /dev/null +++ b/module.h @@ -0,0 +1,8 @@ + +int succ(int x); + +struct exports { + int (*succ)(int); +}; + +extern struct exports module;