Skip to content

Commit

Permalink
Printing unsigned 64-bit integers (#492)
Browse files Browse the repository at this point in the history
Ints in abra are 64-bit signed values, but it's also useful to treat
those 64 bits as an unsigned value. There's currently no `UInt` type,
because that would require a lot more logic with arithmetic and casting
and dealing with overflows, but as a temporary stopgap, this introduces
the `Int#unsignedToString` method (which uses `"%llu"` under the hood as
the print formatter). This also updates the existing `Int#toString`
implementation to use `"%lld"` (it had previously been using `"%d"`...).

This also adds the `-fcall-saved-x18` flag to the flags passed to `clang`
when compiling the assembly output of qbe. This is the clang version of
the [`-ffixed-x18`](https://developer.arm.com/documentation/101754/0623/armclang-Reference/armclang-Command-line-Options/-ffixed-x18)
flag from the arm compiler, which ensures that `x18` is preserved.
Without this flag, this register can potentially be overwritten by the
platform (in this case, macos) even though qbe treats it as a
general-purpose register. This was the source of an extremely mysterious
and frustrating bug.
  • Loading branch information
kengorab authored Nov 14, 2024
1 parent 5b391ff commit 6282840
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 2 deletions.
2 changes: 1 addition & 1 deletion projects/compiler/scripts/abraw
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ qbe -o "$dotabradir/$outfile.s" "$dotabradir/$outfile.ssa"
CFLAGS="-Wno-override-module"
if [[ "$OSTYPE" == "darwin"* ]]; then
# Ignore linker warnings. For some reason libgc.a has warnings on macos
CFLAGS="$CFLAGS -Wl,-w"
CFLAGS="$CFLAGS -Wl,-w -fcall-saved-x18"
fi

clang "$dotabradir/$outfile.s" $abra_root/include/libgc.a -o "$dotabradir/$outfile" -lm $CFLAGS
Expand Down
20 changes: 19 additions & 1 deletion projects/compiler/src/compiler.abra
Original file line number Diff line number Diff line change
Expand Up @@ -3134,6 +3134,24 @@ export type Compiler {

Ok(argVal)
}
"u64_to_string" => {
self._currentFn.block.addComment("begin u64_to_string...")
val _arg = if arguments[0] |arg| arg else unreachable("'u64_to_string' has 1 required argument")
val arg = if _arg |arg| arg else unreachable("'u64_to_string' has 1 required argument")
val argVal = try self._compileExpression(arg)

val intFmtPtr = self._builder.buildGlobalString("%llu")
val sizeVal = try self._currentFn.block.buildCall(Callable.Function(self._snprintf), [Value.Int(0), Value.Int(0), intFmtPtr, argVal]) else |e| return qbeError(e)
val mallocSizeVal = try self._currentFn.block.buildAdd(Value.Int(1), sizeVal) else |e| return qbeError(e)

val mem = try self._currentFn.block.buildCall(Callable.Function(self._malloc), [mallocSizeVal]) else |e| return qbeError(e)
try self._currentFn.block.buildCall(Callable.Function(self._snprintf), [mem, mallocSizeVal, intFmtPtr, argVal]) else |e| return qbeError(e)
val str = try self._constructString(mem, sizeVal)

self._currentFn.block.addComment("...u64_to_string end")

Ok(str)
}
"int_as_float" => {
self._currentFn.block.addComment("begin int_as_float...")

Expand Down Expand Up @@ -3558,7 +3576,7 @@ export type Compiler {
fnVal.addComment("Int#toString(self): String")
val selfParam = fnVal.addParameter("self", intTypeQbe)

val intFmtPtr = self._builder.buildGlobalString("%d")
val intFmtPtr = self._builder.buildGlobalString("%lld")
val sizeVal = try fnVal.block.buildCall(Callable.Function(self._snprintf), [Value.Int(0), Value.Int(0), intFmtPtr, selfParam]) else |e| return qbeError(e)
val mallocSizeVal = try fnVal.block.buildAdd(Value.Int(1), sizeVal) else |e| return qbeError(e)

Expand Down
15 changes: 15 additions & 0 deletions projects/compiler/test/compiler/ints.abra
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,18 @@ println(17 >= 15)
println(17 >= 18.0)
/// Expect: true
println(17 >= 15.1)

// Int#unsignedToString
(() => {
val i1 = 118
/// Expect: 118 118
println(i1.toString(), i1.unsignedToString())

val i2 = (1 << 63) || (1 << 31)
/// Expect: -9223372034707292160 9223372039002259456
println(i2.toString(), i2.unsignedToString())

// val i3 = 0b1000000000000000000000000000000010000000000000000000000000000000
// /// Expect: -9223372034707292160 9223372039002259456
// println(i3.toString(), i3.unsignedToString())
})()
3 changes: 3 additions & 0 deletions projects/std/src/_intrinsics.abra
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ export func argc(): Int
@Intrinsic("argv")
export func argv(): Pointer<Pointer<Byte>>

@Intrinsic("u64_to_string")
export func u64ToString(i: Int): String

@Intrinsic("int_as_float")
export func intAsFloat(i: Int): Float

Expand Down
2 changes: 2 additions & 0 deletions projects/std/src/prelude.abra
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ type Int {
}
pow
}

func unsignedToString(self): String = intrinsics.u64ToString(self)
}

type Float {
Expand Down

0 comments on commit 6282840

Please sign in to comment.