finish reorganisation
implement better comptime configuration & target handling adjust build script
This commit is contained in:
parent
7d678d8c0f
commit
70a81a3dd9
57
README.md
57
README.md
|
@ -1,36 +1,49 @@
|
|||
# Lily
|
||||
an attempt at a cross-platform standard library for hblang.<br>
|
||||
use `./build -h` to see available arguments. supports:
|
||||
- changing target
|
||||
- custom linker (for `c_native`)
|
||||
- running the executable (for `c_native`)
|
||||
- setting output path
|
||||
- dumping assembly of `c_native` or `hbvm_ableos` programs
|
||||
- only recompiling if either source or environment change
|
||||
an attempt at a cross-platform standard library for hblang.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> all features, targets, etc, are provisional and subject to change
|
||||
> all features, targets, modules, etc, are provisional and may be subject to change or deletion
|
||||
|
||||
use `./build -h` to see available arguments.
|
||||
### supports:
|
||||
- changing target
|
||||
- custom linker (for native targets)
|
||||
- running the executable (for native targets)
|
||||
- setting output path
|
||||
- dumping assembly representation
|
||||
- only recompiling if either source or environment change
|
||||
|
||||
### To change build target
|
||||
manually:
|
||||
> create the file `src/lib/target/target.hb` with the contents `target := @use("my_target.hb")`
|
||||
use the `-t` flag supplied in `./build`
|
||||
|
||||
automatically:
|
||||
> use the `-t` flag supplied in `./build`
|
||||
use a target triple (i.e. `x86_64-unknown-linux-gnu`), or pick from one of these aliases:
|
||||
>- hbvm
|
||||
>- ableos
|
||||
>- libc (links to system libc)
|
||||
|
||||
currently available targets:
|
||||
- `c_native`
|
||||
- `hbvm_ableos`
|
||||
>[!NOTE]
|
||||
`hbvm == ableos` (for now)
|
||||
|
||||
### Currently "working" features
|
||||
### Modifying build config
|
||||
compiler flags are in: `./build` (at top of file)
|
||||
> used for NON-CODE configuration
|
||||
|
||||
compile-time configuration is in: `./src/lily/lib.hb`
|
||||
> used for things like toggling debug assertions, setting minimum log level, etc
|
||||
## Features
|
||||
|
||||
### Working
|
||||
- `lily.{Type, TypeOf, Kind, exit, panic, memcpy, memmove, memset}`
|
||||
- `lily.log.{log, info, error, warn, debug, trace}`
|
||||
- `lily.result.Result`
|
||||
- `lily.alloc.{SimpleAllocator, RawAllocator}`
|
||||
- `lily.collections.Vec`
|
||||
|
||||
### Currently "in progress" features
|
||||
### In progress
|
||||
- `lily.rand.SimpleRandom`
|
||||
|
||||
### Currently "broken due to compiler" features
|
||||
- `lily.log.{print, printf}`
|
||||
- `lily.collections.SparseVec`
|
||||
- `lily.alloc.{SimpleAllocator, RawAllocator}`
|
||||
### Partially broken due to compiler
|
||||
- `lily.log.print`
|
||||
|
||||
### Completely broken due to compiler
|
||||
- `lily.log.printf`
|
||||
|
|
151
build
151
build
|
@ -5,18 +5,14 @@ SCRIPT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd)"
|
|||
readonly SCRIPT_DIR
|
||||
readonly HBC_COMMIT="18e8a831"
|
||||
readonly SRC_DIR="$SCRIPT_DIR/src"
|
||||
readonly DEFAULT_OUT="$SCRIPT_DIR/target/lily"
|
||||
readonly BUILD_PATH="$SCRIPT_DIR/target/build"
|
||||
readonly CHECKSUM_FILE="$BUILD_PATH/checksum"
|
||||
readonly CONFIG_FILE="$SRC_DIR/lib/target/config.hb"
|
||||
|
||||
hbc_flags="--backend-flags=opt_level=speed"
|
||||
linker=""
|
||||
target="c_native"
|
||||
target="default"
|
||||
run=0
|
||||
debug="false"
|
||||
dump_asm=0
|
||||
out="$DEFAULT_OUT"
|
||||
|
||||
die() {
|
||||
printf "ERROR: %s\n" "$1" >&2
|
||||
|
@ -46,9 +42,9 @@ check_hbc() {
|
|||
|
||||
check_changes() {
|
||||
[ -r "$CHECKSUM_FILE" ] && previous_checksum=$(cat "$CHECKSUM_FILE")
|
||||
current_checksum=$(printf "%s%s%s%s%s" \
|
||||
current_checksum=$(printf "%s%s%s" \
|
||||
"$(find "$SRC_DIR" -type f -exec md5sum {} + | sort | md5sum)" \
|
||||
"$linker" "$out" "$target" "$debug" | md5sum)
|
||||
"$linker" "$target" | md5sum)
|
||||
echo "$current_checksum" >"$CHECKSUM_FILE"
|
||||
[ "${previous_checksum:-}" != "$current_checksum" ]
|
||||
}
|
||||
|
@ -59,45 +55,28 @@ parse_args() {
|
|||
-h | --help)
|
||||
cat <<EOF
|
||||
Usage: $0 [options]
|
||||
-r, --run Run the compiled output (Available only on target=c_native)
|
||||
-d, --debug Enable debug mode in compilation
|
||||
-a, --dump-asm Dump the assembly to stdout (Available on all targets)
|
||||
-l, --linker <linker> Specify linker (Available only on target=c_native)
|
||||
-t, --target <target> Specify target (Run without <target> to see options)
|
||||
-o, --out <path> Set output path
|
||||
-r, --run Run the output binary
|
||||
-a, --dump-asm Dump the assembly to stdout
|
||||
-l, --linker <linker> Specify linker
|
||||
-t, --target <target> Specify target triple (or an alias: 'libc' 'ableos' 'hbvm')
|
||||
-h, --help Show this help message
|
||||
Examples:
|
||||
$0 -t hbvm_ableos
|
||||
$0 -r -l 'zig cc' -t c_native
|
||||
$0 -r --debug
|
||||
$0 -a -t hbvm_ableos > out.hbf
|
||||
$0 -t ableos
|
||||
$0 -r -l 'zig cc' -t libc
|
||||
$0 -a -t ableos > out.hbf
|
||||
EOF
|
||||
exit
|
||||
;;
|
||||
-r | --run) run=1 ;;
|
||||
-a | --dump-asm) dump_asm=1 ;;
|
||||
-d | --debug) debug="true" ;;
|
||||
-t | --target)
|
||||
[ -z "${2:-}" ] &&
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 --target <target>
|
||||
hbvm_ableos Build for AbleOS
|
||||
c_native Build for your machine
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
[ -z "${2:-}" ] && die "'--target' requires a non-empty option argument"
|
||||
target=$2
|
||||
shift
|
||||
;;
|
||||
--target=?*) target=${1#*=} ;;
|
||||
--target=)
|
||||
cat <<EOF
|
||||
Usage: $0 --target <target>
|
||||
hbvm_ableos Build for AbleOS
|
||||
c_native Build for your machine
|
||||
EOF
|
||||
exit
|
||||
die "'--target' requires a non-empty option argument"
|
||||
;;
|
||||
-l | --linker)
|
||||
[ -z "${2:-}" ] && die "'--linker' requires a non-empty option argument"
|
||||
|
@ -122,58 +101,58 @@ EOF
|
|||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -n "$linker" ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
die "Linker cannot be set with target 'hbvm_ableos'"
|
||||
fi
|
||||
if [ "$run" = 1 ] && [ "$target" = "hbvm_ableos" ]; then
|
||||
die "./build does not support running the executable with target 'hbvm_ableos'"
|
||||
fi
|
||||
if [ "$run" = 1 ] && [ "$dump_asm" = 1 ]; then
|
||||
die "Argument '--run' is incompatible with argument '--dump-asm'"
|
||||
fi
|
||||
case $target in
|
||||
hbvm | ableos)
|
||||
if [ $run = 1 ]; then die "'--run' cannot be set with '--target $target'"; fi
|
||||
target="unknown-virt-unknown"
|
||||
;;
|
||||
libc) target="default" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
build_c_native() {
|
||||
if [ -z "$linker" ]; then
|
||||
for cmd in "zig cc" clang gcc; do
|
||||
if command -v "${cmd%% *}" >/dev/null 2>&1; then
|
||||
linker=$cmd
|
||||
break
|
||||
build() {
|
||||
if check_changes || [ ! -e "$BUILD_PATH/$target/lily" ]; then
|
||||
if [ "$target" = "unknown-virt-unknown" ]; then
|
||||
out="$BUILD_PATH/$target/lily.axe"
|
||||
if [ $dump_asm = 1 ]; then
|
||||
hbc_flags="$hbc_flags --dump-asm"
|
||||
out="/dev/stdout"
|
||||
fi
|
||||
done
|
||||
[ -z "$linker" ] && die "Could not find zig, clang, or gcc. Please install any, or add to PATH."
|
||||
mkdir -p "$BUILD_PATH/$target" || die "Failed to create build directory"
|
||||
[ -w "$BUILD_PATH/$target" ] || die "$BUILD_PATH/$target is not writable"
|
||||
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags "--target=unknown-virt-unknown" >"$out" ||
|
||||
{
|
||||
rm -f "$BUILD_PATH/$target/lily"
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
else
|
||||
if [ -z "$linker" ]; then
|
||||
for cmd in "zig cc" clang gcc; do
|
||||
if command -v "${cmd%% *}" >/dev/null 2>&1; then
|
||||
linker=$cmd
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -z "$linker" ] && die "Could not find zig, clang, or gcc. Please install any, or add to PATH."
|
||||
fi
|
||||
if [ ! "$target" = "default" ]; then
|
||||
hbc_flags="$hbc_flags --target=$target"
|
||||
fi
|
||||
mkdir -p "$BUILD_PATH/$target" || die "Failed to create build directory"
|
||||
[ -w "$BUILD_PATH/$target" ] || die "$BUILD_PATH/$target is not writable"
|
||||
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$BUILD_PATH/$target/lily.o" ||
|
||||
{
|
||||
rm -f "$BUILD_PATH/$target/lily.o"
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
$linker "$BUILD_PATH/$target/lily.o" -o "$BUILD_PATH/$target/lily"
|
||||
fi
|
||||
fi
|
||||
|
||||
if check_changes || [ ! -e "$out" ]; then
|
||||
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$BUILD_PATH/out.o" ||
|
||||
{
|
||||
rm -f "$BUILD_PATH/out.o"
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
|
||||
$linker "$BUILD_PATH/out.o" -o "$out"
|
||||
strip -s "$out" || die "Stripping failed"
|
||||
fi
|
||||
|
||||
if [ "$run" = 1 ]; then exec "$out"; fi
|
||||
if [ "$dump_asm" = 1 ]; then objdump -d "$BUILD_PATH/out.o"; fi
|
||||
}
|
||||
|
||||
build_hbvm() {
|
||||
[ "$out" = "$DEFAULT_OUT" ] && out="$SCRIPT_DIR/target/lily.axe"
|
||||
hbc_flags="$hbc_flags --target=unknown-virt-unknown"
|
||||
if [ "$dump_asm" = 1 ]; then
|
||||
hbc_flags="$hbc_flags --dump-asm"
|
||||
out=/dev/stdout
|
||||
fi
|
||||
if check_changes || [ ! -w "$out" ]; then
|
||||
hbc "$SCRIPT_DIR/src/main.hb" $hbc_flags >"$out" ||
|
||||
{
|
||||
rm -f "$CHECKSUM_FILE"
|
||||
die "Compilation failed"
|
||||
}
|
||||
if [ ! "$target" = "unknown-virt-unknown" ]; then
|
||||
if [ "$run" = 1 ]; then exec "$BUILD_PATH/$target/lily"; fi
|
||||
if [ "$dump_asm" = 1 ]; then objdump -d "$BUILD_PATH/$target/lily.o"; fi
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -182,17 +161,9 @@ main() {
|
|||
|
||||
mkdir -p "$BUILD_PATH" || die "Failed to create build directory"
|
||||
[ -w "$BUILD_PATH" ] || die "$BUILD_PATH is not writable"
|
||||
[ -w "$(dirname "$out")" ] || die "Out directory does not exist or is not writeable"
|
||||
|
||||
printf "target := @use(\"./%s.hb\")\n\$DEBUG := %s" "$target" "$debug" >"$CONFIG_FILE"
|
||||
check_hbc
|
||||
|
||||
case $target in
|
||||
c_native) build_c_native ;;
|
||||
hbvm_ableos) build_hbvm ;;
|
||||
*) die "Invalid target: $target" ;;
|
||||
esac
|
||||
|
||||
build
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
147
src/lib/alloc.hb
147
src/lib/alloc.hb
|
@ -1,147 +0,0 @@
|
|||
.{collections: .{SparseVec, Vec}, target, target_c_native, target_hbvm_ableos, DEBUG, Type, log} := @use("lib.hb")
|
||||
|
||||
Allocation := struct {
|
||||
ptr: ^void,
|
||||
len: uint,
|
||||
}
|
||||
|
||||
// ! THIS ALLOCATOR IS TEMPORARY
|
||||
SimpleAllocator := struct {
|
||||
allocations: Vec(Allocation, RawAllocator),
|
||||
raw: RawAllocator,
|
||||
|
||||
$new := fn(): Self {
|
||||
raw := RawAllocator.new()
|
||||
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
|
||||
}
|
||||
deinit := fn(self: ^Self): void {
|
||||
loop if self.allocations.len() == 0 break else {
|
||||
alloced := self.allocations.pop()
|
||||
if alloced == null continue
|
||||
if target == target_c_native {
|
||||
target.free(alloced.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
target.free(alloced.ptr, alloced.len)
|
||||
}
|
||||
}
|
||||
self.allocations.deinit()
|
||||
self.raw.deinit()
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := target.malloc(count * @sizeof(T))
|
||||
if target == target_hbvm_ableos {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
if DEBUG {
|
||||
log.debug("allocated")
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := target.calloc(count * @sizeof(T))
|
||||
if target == target_hbvm_ableos {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
if target == target_hbvm_ableos {
|
||||
// temporary optimisation, ableos only gives whole pages.
|
||||
// this prevents reallocating 1 page over and over
|
||||
if count * @sizeof(T) < target.PAGE_SIZE return ptr
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced == null return null
|
||||
new_ptr := target.realloc(@bitcast(ptr), alloced.len, count * @sizeof(T))
|
||||
if new_ptr != null self.allocations.push(.(new_ptr, count * @sizeof(T))) else self.allocations.push(alloced)
|
||||
return @bitcast(new_ptr)
|
||||
} else if target == target_c_native {
|
||||
new_ptr := target.realloc(@bitcast(ptr), count * @sizeof(T))
|
||||
if DEBUG {
|
||||
log.debug("reallocated")
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
}
|
||||
}
|
||||
free := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
if target == target_c_native {
|
||||
target.free(@bitcast(ptr))
|
||||
if DEBUG {
|
||||
log.debug("freed")
|
||||
}
|
||||
} else if target == target_hbvm_ableos {
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced == null return;
|
||||
target.free(@bitcast(ptr), alloced.len)
|
||||
}
|
||||
}
|
||||
_find_and_remove := fn(self: ^Self, ptr: ^void): ?Allocation {
|
||||
i := 0
|
||||
loop if i == self.allocations.len() break else {
|
||||
defer i += 1
|
||||
alloced := self.allocations.get(i)
|
||||
if alloced == null return null
|
||||
if alloced.ptr == ptr {
|
||||
_ = self.allocations.remove(i)
|
||||
return alloced
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// ! THIS ALLOCATOR IS *ALSO* TEMPORARY
|
||||
RawAllocator := struct {
|
||||
ptr: ^void,
|
||||
size: uint,
|
||||
$new := fn(): Self return .(Type(^void).uninit(), 0)
|
||||
deinit := fn(self: ^Self): void {
|
||||
if target == target_c_native {
|
||||
// ! (compiler) bug: comparing `self.ptr != Type(^void).uninit()` rather than `self.size == 0`
|
||||
// causes condition to never be true (even though it is)
|
||||
if self.size != 0 target.free(self.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
if self.size != 0 target.free(self.ptr, self.size)
|
||||
};
|
||||
*self = Self.new()
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := target.malloc(count * @sizeof(T))
|
||||
if ptr != null {
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := target.calloc(count * @sizeof(T))
|
||||
if ptr != null {
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
if target == target_hbvm_ableos {
|
||||
new_ptr := target.realloc(self.ptr, self.size, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.ptr = new_ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
} else if target == target_c_native {
|
||||
new_ptr := target.realloc(self.ptr, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.ptr = new_ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
}
|
||||
}
|
||||
free := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
if target == target_c_native {
|
||||
target.free(self.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
target.free(self.ptr, self.size)
|
||||
}
|
||||
}
|
||||
}
|
157
src/lily/alloc.hb
Normal file
157
src/lily/alloc.hb
Normal file
|
@ -0,0 +1,157 @@
|
|||
std := @use("lib.hb");
|
||||
.{Config, Target, Type, log, collections} := std;
|
||||
.{Vec} := collections
|
||||
|
||||
Allocation := struct {
|
||||
ptr: ^void,
|
||||
len: uint,
|
||||
}
|
||||
|
||||
// ! THIS ALLOCATOR IS TEMPORARY
|
||||
SimpleAllocator := struct {
|
||||
allocations: Vec(Allocation, RawAllocator),
|
||||
raw: RawAllocator,
|
||||
|
||||
$new := fn(): Self {
|
||||
raw := RawAllocator.new()
|
||||
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
|
||||
}
|
||||
deinit := fn(self: ^Self): void {
|
||||
loop if self.allocations.len() == 0 break else {
|
||||
alloced := self.allocations.pop()
|
||||
if alloced == null continue
|
||||
match Target.current() {
|
||||
.LibC => Target.free(alloced.ptr),
|
||||
.AbleOS => Target.free(alloced.ptr, alloced.len),
|
||||
}
|
||||
}
|
||||
|
||||
self.allocations.deinit()
|
||||
self.raw.deinit()
|
||||
log.debug("deinit: allocator")
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.malloc(count * @sizeof(T))
|
||||
if Target.current() == .AbleOS {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
log.debug("allocated")
|
||||
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.calloc(count * @sizeof(T))
|
||||
if Target.current() == .AbleOS {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
match Target.current() {
|
||||
.AbleOS => {
|
||||
// temporary optimisation, ableos only gives whole pages.
|
||||
// this prevents reallocating 1 page over and over
|
||||
if count * @sizeof(T) < Target.PAGE_SIZE return ptr
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced == null return null
|
||||
new_ptr := Target.realloc(@bitcast(ptr), alloced.len, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.allocations.push(.(new_ptr, count * @sizeof(T)))
|
||||
} else {
|
||||
self.allocations.push(alloced)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
.LibC => {
|
||||
new_ptr := Target.realloc(@bitcast(ptr), count * @sizeof(T))
|
||||
log.debug("reallocated")
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
}
|
||||
}
|
||||
free := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
match Target.current() {
|
||||
.AbleOS => {
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced != null Target.free(@bitcast(ptr), alloced.len)
|
||||
},
|
||||
.LibC => {
|
||||
Target.free(@bitcast(ptr))
|
||||
},
|
||||
}
|
||||
log.debug("freed")
|
||||
}
|
||||
_find_and_remove := fn(self: ^Self, ptr: ^void): ?Allocation {
|
||||
i := 0
|
||||
loop if i == self.allocations.len() break else {
|
||||
defer i += 1
|
||||
alloced := self.allocations.get(i)
|
||||
if alloced == null return null
|
||||
if alloced.ptr == ptr {
|
||||
_ = self.allocations.remove(i)
|
||||
return alloced
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// ! THIS ALLOCATOR IS *ALSO* TEMPORARY
|
||||
RawAllocator := struct {
|
||||
ptr: ^void,
|
||||
size: uint,
|
||||
$new := fn(): Self return .(Type(^void).uninit(), 0)
|
||||
deinit := fn(self: ^Self): void {
|
||||
self.free(void, Type(^void).uninit());
|
||||
*self = Self.new()
|
||||
log.debug("deinit: raw allocator")
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.malloc(count * @sizeof(T))
|
||||
if ptr != null {
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
log.debug("allocated raw")
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.calloc(count * @sizeof(T))
|
||||
if ptr != null {
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
if self.size == 0 return null
|
||||
log.debug("reallocated raw")
|
||||
match Target.current() {
|
||||
.LibC => {
|
||||
new_ptr := Target.realloc(self.ptr, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.ptr = new_ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
.AbleOS => {
|
||||
new_ptr := Target.realloc(self.ptr, self.size, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.ptr = new_ptr
|
||||
self.size = count * @sizeof(T)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
}
|
||||
}
|
||||
// ! INLINING THIS FUNCTION CAUSES MISCOMPILATION!! DO NOT INLINE IT!! :) :) :)
|
||||
free := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
if self.size == 0 return;
|
||||
match Target.current() {
|
||||
.LibC => Target.free(self.ptr),
|
||||
.AbleOS => Target.free(self.ptr, self.size),
|
||||
}
|
||||
log.debug("freed raw")
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.{target, target_c_native, target_hbvm_ableos, memmove, Type} := @use("../lib.hb");
|
||||
.{memmove, Type, log, alloc} := @use("../lib.hb");
|
||||
|
||||
Vec := fn($T: type, $A: type): type return struct {
|
||||
slice: []T,
|
||||
|
@ -10,6 +10,11 @@ Vec := fn($T: type, $A: type): type return struct {
|
|||
if self.cap > 0 self.allocator.free(T, self.slice.ptr)
|
||||
self.slice = Type([]T).uninit()
|
||||
self.cap = 0
|
||||
if A == alloc.RawAllocator {
|
||||
log.debug("deinit: vec (w/ raw allocator)")
|
||||
} else {
|
||||
log.debug("deinit: vec (w/ allocator)")
|
||||
}
|
||||
}
|
||||
push := fn(self: ^Self, value: T): void {
|
||||
if self.slice.len == self.cap {
|
89
src/lily/lib.hb
Normal file
89
src/lily/lib.hb
Normal file
|
@ -0,0 +1,89 @@
|
|||
Version := struct {
|
||||
major: uint,
|
||||
minor: uint,
|
||||
patch: uint,
|
||||
}
|
||||
|
||||
$VERSION := Version(0, 0, 4)
|
||||
|
||||
target_ableos := @use("targets/ableos.hb")
|
||||
target_libc := @use("targets/libc.hb")
|
||||
|
||||
Config := struct {
|
||||
$DEBUG := false
|
||||
$DEBUG_ASSERTIONS := false
|
||||
|
||||
$MIN_LOGLEVEL := log.LogLevel.Info
|
||||
|
||||
$debug := fn(): bool return Config.DEBUG
|
||||
$debug_assertions := fn(): bool return Config.DEBUG | Config.DEBUG_ASSERTIONS
|
||||
$min_loglevel := fn(): log.LogLevel {
|
||||
if Config.debug() return .Debug
|
||||
return Config.MIN_LOGLEVEL
|
||||
}
|
||||
}
|
||||
|
||||
Target := enum {
|
||||
LibC,
|
||||
AbleOS,
|
||||
|
||||
$current := fn(): Self {
|
||||
// This captures all HBVM targets, but for now only AbleOS is supported
|
||||
if @target("*-virt-unknown") {
|
||||
return .AbleOS
|
||||
}
|
||||
// Assume that unknown targets have libc
|
||||
return .LibC
|
||||
}
|
||||
$Lib := fn(): type {
|
||||
match Self.current() {
|
||||
.LibC => return target_libc,
|
||||
.AbleOS => return target_ableos,
|
||||
}
|
||||
}
|
||||
/* ! memmove, memcpy, memset, exit, currently suffixed with `_w` to distinguish them from the wrapper functions */;
|
||||
.{malloc, calloc, realloc, free, memmove: memmove_w, memcpy: memcpy_w, memset: memset_w, exit: exit_w, getrandom} := Target.Lib();
|
||||
.{printf_str} := Target.Lib();
|
||||
.{PAGE_SIZE, LogMsg} := Target.Lib()
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
||||
collections := @use("collections/lib.hb")
|
||||
result := @use("result.hb")
|
||||
string := @use("string.hb")
|
||||
alloc := @use("alloc.hb")
|
||||
rand := @use("rand.hb")
|
||||
log := @use("log.hb")
|
||||
fmt := @use("fmt.hb");
|
||||
|
||||
.{print, printf} := log;
|
||||
.{Type, TypeOf} := @use("type.hb")
|
||||
|
||||
// ! (compiler) bug: inlining here crashes the parser. nice.
|
||||
// ! (c_native) (compiler) bug: NOT inlining here makes it sometimes not work
|
||||
$panic := fn(message: ?[]u8): never {
|
||||
if message != null log.error(message) else log.error("The program called panic.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// ! exit, memcpy, memmove, and memset are all temporary wrapper functions
|
||||
$exit := fn(code: int): never {
|
||||
Target.exit_w(code)
|
||||
die
|
||||
}
|
||||
|
||||
$memcpy := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memcpy requires a pointer")
|
||||
Target.memcpy_w(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memmove := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memmove requires a pointer")
|
||||
Target.memmove_w(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memset := fn(dest: @Any(), src: u8, size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer @error("memset requires a pointer")
|
||||
Target.memset_w(@bitcast(dest), src, size)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.{target, target_c_native, target_hbvm_ableos, fmt} := @use("lib.hb")
|
||||
.{Config, Target, fmt} := @use("lib.hb")
|
||||
|
||||
LogLevel := enum {
|
||||
Error,
|
||||
|
@ -9,28 +9,27 @@ LogLevel := enum {
|
|||
}
|
||||
|
||||
log := fn(level: LogLevel, str: []u8): void {
|
||||
if target == target_hbvm_ableos {
|
||||
return @eca(3, 1, target.LogMsg.(level, str.ptr, str.len), @sizeof(target.LogMsg))
|
||||
} else if target == target_c_native {
|
||||
match level {
|
||||
.Error => target.printf_str("\{1b}[31mERROR\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Warn => target.printf_str("\{1b}[33mWARN\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Info => target.printf_str("\{1b}[32mINFO\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Debug => target.printf_str("\{1b}[34mDEBUG\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Trace => target.printf_str("\{1b}[35mTRACE\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
}
|
||||
if level > Config.min_loglevel() return;
|
||||
match Target.current() {
|
||||
.LibC => match level {
|
||||
.Error => Target.printf_str("\{1b}[31mERROR\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Warn => Target.printf_str("\{1b}[33mWARN\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Info => Target.printf_str("\{1b}[32mINFO\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Debug => Target.printf_str("\{1b}[34mDEBUG\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
.Trace => Target.printf_str("\{1b}[35mTRACE\{1b}[0m: %.*s\n\0".ptr, str.len, str.ptr),
|
||||
},
|
||||
.AbleOS => return @eca(3, 1, Target.LogMsg.(level, str.ptr, str.len), @sizeof(Target.LogMsg)),
|
||||
}
|
||||
}
|
||||
|
||||
// it's good enough i guess. dont write more than 4096 chars or you will explode.
|
||||
print_buffer := @embed("../assets/zeroed")
|
||||
print_buffer := @embed("assets/zeroed")
|
||||
|
||||
print := fn(any: @Any()): void {
|
||||
if @TypeOf(any) == []u8 {
|
||||
if target == target_c_native {
|
||||
target.printf_str("%.*s\n\0".ptr, any.len, any.ptr)
|
||||
} else if target == target_hbvm_ableos {
|
||||
info(any)
|
||||
match Target.current() {
|
||||
.LibC => Target.printf_str("%.*s\n\0".ptr, any.len, any.ptr),
|
||||
.AbleOS => info(any),
|
||||
}
|
||||
} else {
|
||||
// limits len to size of buffer - 1
|
|
@ -1,4 +1,4 @@
|
|||
.{target, TypeOf, Type} := @use("lib.hb")
|
||||
.{Target, TypeOf, Type} := @use("lib.hb")
|
||||
|
||||
// ! NON CRYPTOGRAPHIC, TEMPORARY
|
||||
SimpleRandom := struct {
|
||||
|
@ -17,11 +17,11 @@ SimpleRandom := struct {
|
|||
}
|
||||
if T.is_bool() {
|
||||
a: A = idk
|
||||
target.getrandom(@bitcast(&a), 1)
|
||||
Target.getrandom(@bitcast(&a), 1)
|
||||
return @bitcast(a & @as(u8, 1))
|
||||
}
|
||||
a: A = idk
|
||||
target.getrandom(@bitcast(&a), T.size())
|
||||
Target.getrandom(@bitcast(&a), T.size())
|
||||
return a
|
||||
}
|
||||
/// Accepts any type for min and max (as long as it is the same for both).
|
||||
|
@ -32,8 +32,8 @@ SimpleRandom := struct {
|
|||
$fill := fn(self: ^Self, buf: @Any()): void {
|
||||
T := TypeOf(buf)
|
||||
match T.kind() {
|
||||
.Slice => target.getrandom(@bitcast(buf.ptr), buf.len * T.Child().size()),
|
||||
.Array => target.getrandom(@bitcast(&buf), T.size()),
|
||||
.Slice => Target.getrandom(@bitcast(buf.ptr), buf.len * T.Child().size()),
|
||||
.Array => Target.getrandom(@bitcast(&buf), T.size()),
|
||||
_ => @error("Can only fill bytes of Slice or Array."),
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
.{log: .{LogLevel}} := @use("../lib.hb")
|
||||
.{LogLevel} := @use("../lib.hb").std.log
|
||||
|
||||
$PAGE_SIZE := 4096
|
||||
|
|
@ -1,52 +1,3 @@
|
|||
Version := struct {
|
||||
major: uint,
|
||||
minor: uint,
|
||||
patch: uint,
|
||||
}
|
||||
|
||||
$VERSION := Version(0, 0, 3)
|
||||
|
||||
collections := @use("collections/lib.hb")
|
||||
result := @use("result.hb")
|
||||
string := @use("string.hb")
|
||||
alloc := @use("alloc.hb")
|
||||
rand := @use("rand.hb")
|
||||
log := @use("log.hb")
|
||||
fmt := @use("fmt.hb")
|
||||
|
||||
target_hbvm_ableos := @use("target/hbvm_ableos.hb")
|
||||
target_c_native := @use("target/c_native.hb");
|
||||
.{DEBUG, target} := @use("target/config.hb")
|
||||
|
||||
// ! exit, memcpy, memmove, and memset are all temporary wrapper functions
|
||||
|
||||
$exit := fn(code: int): never {
|
||||
target.exit(code)
|
||||
die
|
||||
}
|
||||
|
||||
// ! (compiler) bug: inlining here crashes the parser. nice.
|
||||
// ! (c_native) (compiler) bug: NOT inlining here makes it sometimes not work
|
||||
$panic := fn(message: ?[]u8): never {
|
||||
if message != null log.error(message) else log.error("The program called panic.")
|
||||
exit(1)
|
||||
}
|
||||
|
||||
$memcpy := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memcpy requires a pointer")
|
||||
target.memcpy(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memmove := fn(dest: @Any(), src: @Any(), size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer | TypeOf(src).kind() != .Pointer @error("memmove requires a pointer")
|
||||
target.memmove(@bitcast(dest), @bitcast(src), size)
|
||||
}
|
||||
|
||||
$memset := fn(dest: @Any(), src: u8, size: uint): void {
|
||||
if TypeOf(dest).kind() != .Pointer @error("memset requires a pointer")
|
||||
target.memset(@bitcast(dest), src, size)
|
||||
}
|
||||
|
||||
RawKind := enum {
|
||||
Builtin,
|
||||
Struct,
|
|
@ -1,4 +1,4 @@
|
|||
lily := @use("lib/lib.hb");
|
||||
lily := @use("lily/lib.hb")
|
||||
|
||||
Allocator := lily.alloc.SimpleAllocator
|
||||
Vec := lily.collections.Vec
|
||||
|
@ -18,13 +18,13 @@ main := fn(argc: uint, argv: []^void): uint {
|
|||
// the program to halt after the loop
|
||||
// ! (compiler) bug: checking against `vec.len` rather than `i` causes the loop to go forever
|
||||
// despite the fact that vec.len is incremented in vec.push
|
||||
loop if i == 5 break else {
|
||||
loop if i == 10 break else {
|
||||
defer i += 1
|
||||
vec.push(rand.any(uint))
|
||||
lily.log.info("pushed to vec")
|
||||
}
|
||||
|
||||
// ! (c_native) this causes a compiler bug in lily.fmt.fmt_int
|
||||
// ! (libc) this causes a compiler bug in lily.fmt.fmt_int
|
||||
// lily.log.print(100)
|
||||
|
||||
lily.log.print(true)
|
||||
|
|
Loading…
Reference in a new issue