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