begin low-level runtime implementation
This commit is contained in:
parent
d10b182479
commit
07c64779d8
9 changed files with 301 additions and 0 deletions
21
llrtc/Makefile
Normal file
21
llrtc/Makefile
Normal file
|
|
@ -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
|
||||||
25
llrtc/README.md
Normal file
25
llrtc/README.md
Normal file
|
|
@ -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
|
||||||
|
|
||||||
26
llrtc/common.mk
Normal file
26
llrtc/common.mk
Normal file
|
|
@ -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
|
||||||
44
llrtc/lib/Makefile
Normal file
44
llrtc/lib/Makefile
Normal file
|
|
@ -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
|
||||||
22
llrtc/lib/test_udivmod64.c
Normal file
22
llrtc/lib/test_udivmod64.c
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
53
llrtc/lib/test_udivmod64.py
Normal file
53
llrtc/lib/test_udivmod64.py
Normal file
|
|
@ -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()
|
||||||
84
llrtc/lib/udivmod64.c
Normal file
84
llrtc/lib/udivmod64.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
11
llrtc/lib/udivmod64.h
Normal file
11
llrtc/lib/udivmod64.h
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef LLRT_UDIVMOD_H_
|
||||||
|
#define LLRT_UDIVMOD_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define BITS_PER_BYTE 8
|
||||||
|
|
||||||
|
uint64_t udivmod64(uint64_t dividend, uint64_t divisor, uint64_t *remainder);
|
||||||
|
|
||||||
|
#endif /* LLRT_UDIVMOD_H_ */
|
||||||
|
|
||||||
15
llrtc/tools/striptriple.py
Normal file
15
llrtc/tools/striptriple.py
Normal file
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue