diff --git a/llrtc/Makefile b/llrtc/Makefile new file mode 100644 index 0000000..c271d0e --- /dev/null +++ b/llrtc/Makefile @@ -0,0 +1,21 @@ +DIRECTORIES = divmod + +all: + for mydir in $(DIRECTORIES); do \ + make -C $$mydir; \ + done + +test: + for mydir in $(DIRECTORIES); do \ + make -C $$mydir test; \ + done + +clean-test: + for mydir in $(DIRECTORIES); do \ + make -C $$mydir clean-test; \ + done + +clean: + for mydir in $(DIRECTORIES); do \ + make -C $$mydir clean; \ + done \ No newline at end of file diff --git a/llrtc/README.md b/llrtc/README.md new file mode 100644 index 0000000..539d9c3 --- /dev/null +++ b/llrtc/README.md @@ -0,0 +1,25 @@ +# LLRT: Low Level Runtime + +## Why? + +The same reason for LLVM compiler-rt. LLVM generates libgcc symbols, such as +__divdi3 for 64-bit division on 32-bit platform. They are not also available. +We need to ship compiler-rt but it is not Windows ready. +This subproject aims to provide a small portable subset of compiler-rt. +Start small and add only the things we really needed. +Performance is not crucial but should not be terrible. +Functionality and usefullness should be more important than performance. + +## Developer Instructions + +LLRT implements some functionalities in compiler-rt in ANSI C. +The C files are compiled using clang to produce LLVM IR which are shipped. +The IR files are committed in the repository. +So, remember to build the IR files commit them after modifying the C files. + +## Build Requirement + +- Make +- Clang +- Python + diff --git a/llrtc/common.mk b/llrtc/common.mk new file mode 100644 index 0000000..3de9c12 --- /dev/null +++ b/llrtc/common.mk @@ -0,0 +1,26 @@ +CLANG = clang +CF = -Wall -ansi +OUTDIR = .. + +all: ir + +$(OUTPUT).c: $(OUTPUT).h + +$(OUTPUT): test.c $(OUTPUT).c + $(CLANG) $(CF) -ftrapv -o $@ $+ + +test: $(OUTPUT) + python test.py + +ir: + CLANG -m32 $(CF) -O0 -emit-llvm -S $(OUTPUT).c -o $(OUTDIR)/x86/$(OUTPUT).ll + CLANG -m64 $(CF) -O0 -emit-llvm -S $(OUTPUT).c -o $(OUTDIR)/x86_64/$(OUTPUT).ll + python ../tools/striptriple.py $(OUTDIR)/x86/$(OUTPUT).ll + python ../tools/striptriple.py $(OUTDIR)/x86_64/$(OUTPUT).ll + +clean-test: + rm -f $(OUTPUT) + +clean: clean-test + rm -f $(OUTDIR)/x86/$(OUTPUT).ll + rm -f $(OUTDIR)/x86_64/$(OUTPUT).ll diff --git a/llrtc/lib/Makefile b/llrtc/lib/Makefile new file mode 100644 index 0000000..1bb325f --- /dev/null +++ b/llrtc/lib/Makefile @@ -0,0 +1,44 @@ +OUTPUT = llrt +SOURCES = udivmod64.c + +CLANG = clang +CF = -Wall -ansi +CF_TEST = $(CF) -ftrapv +CF_BUILD = $(CF) -O0 -emit-llvm +OUTDIR = .. + +all: ir + +ir: $(SOURCES) + $(CLANG) -m32 $(CF_BUILD) -S -o $(OUTDIR)/$(OUTPUT)_x86.ll -c $(SOURCES) + python ../tools/striptriple.py $(OUTDIR)/$(OUTPUT)_x86.ll + $(CLANG) -m64 $(CF_BUILD) -S -o $(OUTDIR)/$(OUTPUT)_x86_64.ll -c $(SOURCES) + python ../tools/striptriple.py $(OUTDIR)/$(OUTPUT)_x86_64.ll + +lib$(OUTPUT).a: $(SOURCES) + $(CLANG) $(CF) -o $@ -c $+ + +build-test: lib$(OUTPUT).a + for src in $(SOURCES); do \ + $(CLANG) $(CF_TEST) -o test_$${src%.*} test_$$src -L. -lllrt; \ + done; + +test: build-test + for src in $(SOURCES); do \ + echo "testing $${src%.*}"; \ + python test_$${src%.*}.py > test_$${src%.*}.out; \ + done; + +clean-test: + rm -f *.out + rm -f *.a + for src in $(SOURCES); do \ + rm -f test_$${src%.*} ;\ + done; + +clean-dist: + rm $(OUTDIR)/*.ll + +clean: clean-test clean-dist + +udivmod64.c: udivmod64.h diff --git a/llrtc/lib/test_udivmod64.c b/llrtc/lib/test_udivmod64.c new file mode 100644 index 0000000..e2935c5 --- /dev/null +++ b/llrtc/lib/test_udivmod64.c @@ -0,0 +1,22 @@ +#include +#include + +extern uint64_t +udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder); + +int main(int argc, char * argv[]){ + uint64_t n, d, q, r; + if (argc != 3) { + printf("invalid argument: %s dividend divisor", argv[0]); + return 1; + } + sscanf(argv[1], "%llu", &n); + sscanf(argv[2], "%llu", &d); + + q = udivmod64(n, d, &r); + + printf("%llu\n", q); + printf("%llu\n", r); + + return 0; +} \ No newline at end of file diff --git a/llrtc/lib/test_udivmod64.py b/llrtc/lib/test_udivmod64.py new file mode 100644 index 0000000..487a458 --- /dev/null +++ b/llrtc/lib/test_udivmod64.py @@ -0,0 +1,53 @@ +import math +import os +import subprocess +udt = os.path.join('.', 'test_udivmod64') + +def testcase(dividend, divisor): + print 'divmod64(%d, %d)' % (dividend, divisor) + + procargs = ('%s %s %s' % (udt, dividend, divisor)).split() + result = subprocess.check_output(procargs) + gotQ, gotR = map(int, result.splitlines()) + + expectQ = dividend // divisor + expectR = dividend % divisor + + print 'Q = %d, R = %d' % (gotQ, gotR) + + if expectQ != gotQ: + raise ValueError("invalid quotient: got=%d but expect=%d" % + (gotQ, expectQ)) + if expectR != gotR: + raise ValueError("invalid remainder: got=%d but expect=%d" % + (gotR, expectR)) + print 'OK' + +def testsequence(): + subjects = [ + (0, 1), + (0, 0xffffffffffffffff), + (1, 2), + (1, 983219), + (2, 2), + (3, 2), + (1024, 2), + (2048, 512), + (21321, 512), + (9329189, 1031), + (0xffffffff, 2), + (0xffffffff, 0xffff), + (0x1ffffffff, 2), + (0x1ffffffff, 0xffff), + (0xffff, 0xffffffff), + (0xffffffffffffffff, 0xffff), + (0xffffffffffffffff, 0x7fffffffffffffff), + (0xffffffffffffffff, 0xfffffffffffffff0), + (0xffffffffffffffff, 87655678587161901), + ] + + for dvd, dvr in subjects: + testcase(dvd, dvr) + +if __name__ == '__main__': + testsequence() diff --git a/llrtc/lib/udivmod64.c b/llrtc/lib/udivmod64.c new file mode 100644 index 0000000..a028aea --- /dev/null +++ b/llrtc/lib/udivmod64.c @@ -0,0 +1,84 @@ +/* +Implements unsigned divmod using for platform missing 64-bit division and/or +modulo functions. +*/ +#include "udivmod64.h" + +/* +count left zero for 64-bit words + +*/ +static +int clz64(uint64_t x) +{ + const int total_bits = sizeof(x) * BITS_PER_BYTE; + int zc = 0; + + while (zc < total_bits && ((x >> (total_bits - zc - 1)) & 1) == 0) { + ++zc; + } + return zc; +} + +typedef struct div_state_ +{ + uint64_t tmp, dvd; +} div_state; + +/* +Left shift div_state by 1 bit +*/ +static +void div_state_lshift(div_state *state) +{ + state->tmp = (state->tmp << 1) | (state->dvd >> 63); + state->dvd = state->dvd << 1; +} + +/* +Division of unsigned 64-bit word using 64-bit addition and subtration following +the shift-restore division algorithm. +For those interested in 32-bit implementation, +mapping of 64-bit addition and subtraction to 32-bit should be trivial. + +Reference: + - IBM. The PowerPC Compiler Writer's Guide + - LLVM compiler-rt + +Assumptions: + - all operands and results are positive + - unsigned wrapped around +*/ +uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder) +{ + div_state state = {0, dividend}; + uint64_t quotient = 0; + int i; + int skipahead; + + if (divisor == 0) { + return 1 / 0; /* intentionally div by zero */ + } + + /* + skipahead to reduce iteration + */ + skipahead = clz64(dividend); + + for (i = 0; i < skipahead; ++i) { + div_state_lshift(&state); + } + + /* + division loop + */ + for (i = skipahead; i < 64; ++i) { + div_state_lshift(&state); + if (state.tmp >= divisor) { + state.tmp = state.tmp - divisor; + quotient |= 1ull << (63 - i); + } + } + *remainder = state.tmp; + return quotient; +} diff --git a/llrtc/lib/udivmod64.h b/llrtc/lib/udivmod64.h new file mode 100644 index 0000000..9cbcd8c --- /dev/null +++ b/llrtc/lib/udivmod64.h @@ -0,0 +1,11 @@ +#ifndef LLRT_UDIVMOD_H_ +#define LLRT_UDIVMOD_H_ + +#include + +#define BITS_PER_BYTE 8 + +uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder); + +#endif /* LLRT_UDIVMOD_H_ */ + diff --git a/llrtc/tools/striptriple.py b/llrtc/tools/striptriple.py new file mode 100644 index 0000000..a20ba34 --- /dev/null +++ b/llrtc/tools/striptriple.py @@ -0,0 +1,15 @@ +import sys +import re + +buf = [] +with open(sys.argv[1], 'r') as fin: + tripleline = re.compile('^target\s+triple\s+=\s+') + for line in fin.readlines(): + if not tripleline.match(line): + buf.append(line) + +with open(sys.argv[1], 'w') as fout: + for line in buf: + fout.write(line) + +