Heap fill/compare; Std string experiments

This commit is contained in:
dcodeIO 2017-12-21 10:14:53 +01:00
parent dd5c3e7a4e
commit 666ba54e42
17 changed files with 5108 additions and 222 deletions

View File

@ -93,9 +93,8 @@ function checkDiagnostics(parser) {
if (!args.noLib) {
var stdlibDir = path.join(__dirname + "..", "std", "assembly");
glob.sync("*.ts", { cwd: stdlibDir }).forEach(file => {
var nextPath = "std/" + file;
var nextText = fs.readFileSync(path.join(stdlibDir, file), { encoding: "utf8" });
parser = assemblyscript.parseFile(nextText, nextPath, parser, false);
parser = assemblyscript.parseFile(nextText, "std:" + file, parser, false);
});
}

View File

@ -11,7 +11,6 @@
"url": "https://github.com/AssemblyScript/next/issues"
},
"dependencies": {
"@types/node": "^8.5.1",
"binaryen": "40.0.0-nightly.20171209",
"glob": "^7.1.2",
"minimist": "^1.2.0",
@ -23,6 +22,7 @@
"@types/glob": "^5.0.34",
"@types/long": "^3.0.32",
"@types/minimist": "^1.2.0",
"@types/node": "^8.5.1",
"chalk": "^2.3.0",
"diff": "^3.4.0",
"long": "^3.2.0",

View File

@ -914,7 +914,13 @@ export enum ElementFlags {
/** Is global. */
GLOBAL = 1 << 11,
/** Is read-only. */
READONLY = 1 << 12
READONLY = 1 << 12,
/** Is a public member. */
PUBLIC = 1 << 13,
/** Is a protected member. */
PROTECTED = 1 << 14,
/** Is a private member. */
PRIVATE = 1 << 15
}
/** Base class of all program elements. */
@ -1441,8 +1447,11 @@ export class FieldPrototype extends Element {
switch (this.declaration.modifiers[i].modifierKind) {
case ModifierKind.EXPORT: this.isExported = true; break;
case ModifierKind.READONLY: this.isReadonly = true; break;
case ModifierKind.PRIVATE:
case ModifierKind.PROTECTED:
case ModifierKind.PUBLIC:
case ModifierKind.STATIC: break; // already handled
default: throw new Error("unexpected modifier");
default: throw new Error("unexpected modifier: " + this.declaration.modifiers[i]);
}
}
}

24
std/assembly.d.ts vendored
View File

@ -191,10 +191,25 @@ declare class Array<T> {
/** Class representing a sequence of characters. */
declare class String {
static fromCharCode(ls: i32, hs?: i32): string;
static fromCharCodes(arr: u16[]): string;
static fromCodePoint(cp: i32): string;
static fromCodePoints(arr: i32[]): string;
readonly length: u32;
charAt(index: u32): string;
charCodeAt(index: u32): u16;
concat(other: string): string;
endsWith(other: string): bool;
indexOf(other: string): u32;
startsWith(other: string): bool;
substr(start: u32, length?: u32): string;
substring(start: u32, end?: u32): string;
trim(): string;
trimLeft(): string;
trimRight(): string;
}
/** Class for representing a runtime error. Base class of all errors. */
@ -237,6 +252,12 @@ declare class Heap {
/** Copies a chunk of memory from one location to another. */
static copy(dest: usize, src: usize, n: usize): usize;
/** Fills a chunk of memory with the specified byte value. */
static fill(dest: usize, c: u8, n: usize): usize;
/** Compares two chunks of memory. Returns `0` if equal, otherwise the difference of the first differing bytes. */
static compare(vl: usize, vr: usize, n: usize): i32;
private constructor();
}
@ -251,3 +272,6 @@ interface RegExp {}
/** Annotates an element being part of the global namespace. */
declare function global(): any;
/** Annotates a method being an operator overload. */
declare function operator(token: string): any;

View File

@ -29,7 +29,7 @@ export class Heap {
// just a big chunk of non-disposable memory for now
}
static copy(dest: usize, src: usize, n: usize): usize {
static copy(dest: usize, src: usize, n: usize): usize { // TODO: use move_memory op once available
assert(dest >= HEAP_BASE);
// the following is based on musl's implementation of memcpy
@ -174,5 +174,76 @@ export class Heap {
return dest;
}
static fill(dest: usize, c: u8, n: usize): usize { // TODO: use set_memory op once available
assert(dest >= HEAP_BASE);
// the following is based on musl's implementation of memset
if (!n) return dest;
let s: usize = dest;
// Fill head and tail with minimal branching
store<u8>(s, c); store<u8>(s + n - 1, c);
if (n <= 2) return dest;
store<u8>(s + 1, c); store<u8>(s + n - 2, c);
store<u8>(s + 2, c); store<u8>(s + n - 3, c);
if (n <= 6) return dest;
store<u8>(s + 3, c); store<u8>(s + n - 4, c);
if (n <= 8) return dest;
// Align to 4 bytes
let k: usize = -s & 3;
s += k;
n -= k;
n &= -4;
let c32: u32 = -1 / 255 * c;
// Fill head and tail in preparation of setting 32 bytes at a time
store<u32>(s, c32);
store<u32>(s + n - 4, c32);
if (n <= 8) return dest;
store<u32>(s + 4, c32);
store<u32>(s + 8, c32);
store<u32>(s + n - 12, c32);
store<u32>(s + n - 8, c32);
if (n <= 24) return dest;
store<u32>(s + 12, c32);
store<u32>(s + 16, c32);
store<u32>(s + 20, c32);
store<u32>(s + 24, c32);
store<u32>(s + n - 28, c32);
store<u32>(s + n - 24, c32);
store<u32>(s + n - 20, c32);
store<u32>(s + n - 16, c32);
// Align to 8 bytes
k = 24 + (s & 4);
s += k;
n -= k;
// Set 32 bytes at a time
let c64: u64 = <u64>c32 | (<u64>c32 << 32);
while (n >= 32) {
store<u64>(s, c64);
store<u64>(s + 8, c64);
store<u64>(s + 16, c64);
store<u64>(s + 24, c64);
n -= 32; s += 32;
}
return dest;
}
static compare(vl: usize, vr: usize, n: usize): i32 {
if (vl == vr) return 0;
// the following is based on musl's implementation of memcmp
while (n && load<u8>(vl) == load<u8>(vr)) {
n--; vl++; vr++;
}
return n ? <i32>load<u8>(vl) - <i32>load<u8>(vr) : 0;
}
private constructor() {}
}

View File

@ -1,4 +1,98 @@
@global()
export class String {
// TODO
private ptr: usize;
readonly length: u32;
constructor(ptr: usize, len: u32) {
this.ptr = ptr;
this.length = len;
}
charAt(index: u32): String {
assert(this != null && index < this.length);
return new String(this.ptr + (index << 1), 1);
}
charCodeAt(index: u32): u16 {
assert(this != null && index < this.length);
return load<u16>(this.ptr + (index << 1));
}
@operator("+")
concat(other: String): String {
assert(this != null && other != null);
const len: u32 = this.length + other.length;
const ptr: usize = Heap.allocate(len << 1);
Heap.copy(ptr, this.ptr, this.length << 1);
Heap.copy(ptr, this.ptr + (len << 1), other.length << 1);
return new String(ptr, len);
}
endsWith(other: String): bool {
assert(this != null && other != null);
if (other.length > this.length)
return false;
for (let i: u32 = this.length - other.length, j: u32 = 0, k: u32 = this.length; i < k;)
if (this.charCodeAt(i++) != other.charCodeAt(j++))
return false;
return true;
}
@operator("==")
equals(other: String): bool {
assert(this != null && other != null);
if (this.length != other.length)
return false;
for (let i: u32 = 0, k: u32 = this.length; i < k; ++i)
if (this.charCodeAt(i) != other.charCodeAt(i))
return false;
return true;
}
indexOf(other: String): u32 {
assert(this != null && other != null);
throw new Error("not implemented");
}
startsWith(other: String): bool {
assert(this != null && other != null);
if (other.length > this.length)
return false;
for (let i: u32 = 0, k: u32 = other.length; i < k; ++i)
if (this.charCodeAt(i) != other.charCodeAt(i))
return false;
return true;
}
substr(start: u32, length: u32 = <u32>-1): String {
assert(this != null);
if (start >= this.length)
return changetype<string,String>("");
const len: u32 = min<u32>(length, this.length - start);
return new String(this.ptr + (start << 1), len);
}
substring(start: u32, end: u32 = <u32>-1): String {
assert(this != null);
if (start >= this.length || end <= start)
return changetype<string,String>("");
const len: u32 = min<u32>(end - start, this.length - start);
return new String(this.ptr + (start << 1), len);
}
trim(): string {
assert(this != null);
throw new Error("not implemented");
}
trimLeft(): string {
assert(this != null);
throw new Error("not implemented");
}
trimRight(): string {
assert(this != null);
throw new Error("not implemented");
}
}

View File

@ -26,7 +26,7 @@ glob.sync(filter, { cwd: __dirname + "/compiler" }).forEach(filename => {
var parser = new Parser();
if (filename.startsWith("std/")) {
stdFiles.forEach(file => {
parser.parseFile(fs.readFileSync(__dirname + "/../std/assembly/" + file, { encoding: "utf8" }), file, false);
parser.parseFile(fs.readFileSync(__dirname + "/../std/assembly/" + file, { encoding: "utf8" }), "std:" + path.basename(file), false);
});
fixture = "std/" + fixture;
}

View File

@ -0,0 +1,31 @@
(module
(type $ii (func (param i32) (result i32)))
(memory $0 1)
(export "fib" (func $recursive/fib))
(export "memory" (memory $0))
(func $recursive/fib (; 0 ;) (type $ii) (param $0 i32) (result i32)
(if
(i32.le_s
(get_local $0)
(i32.const 1)
)
(return
(i32.const 1)
)
)
(i32.add
(call $recursive/fib
(i32.sub
(get_local $0)
(i32.const 1)
)
)
(call $recursive/fib
(i32.sub
(get_local $0)
(i32.const 2)
)
)
)
)
)

View File

@ -0,0 +1,4 @@
export function fib(n: i32): i32 {
if (n <= 1) return 1;
return fib(n - 1) + fib(n - 2);
}

View File

@ -0,0 +1,84 @@
(module
(type $ii (func (param i32) (result i32)))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "fib" (func $recursive/fib))
(export "memory" (memory $0))
(func $recursive/fib (; 0 ;) (type $ii) (param $0 i32) (result i32)
(if
(i32.le_s
(get_local $0)
(i32.const 1)
)
(return
(i32.const 1)
)
)
(return
(i32.add
(call $recursive/fib
(i32.sub
(get_local $0)
(i32.const 1)
)
)
(call $recursive/fib
(i32.sub
(get_local $0)
(i32.const 2)
)
)
)
)
)
)
(;
[program.elements]
NaN
Infinity
isNaN
isFinite
clz
ctz
popcnt
rotl
rotr
abs
max
min
ceil
floor
copysign
nearest
reinterpret
sqrt
trunc
load
store
sizeof
select
unreachable
current_memory
grow_memory
parseInt
parseFloat
changetype
assert
i8
i16
i32
i64
u8
u16
u32
u64
bool
f32
f64
isize
usize
HEAP_BASE
recursive/fib
[program.exports]
recursive/fib
;)

View File

@ -49,36 +49,38 @@
isize
usize
HEAP_BASE
array/Array
std:array/Array
Array
error/Error
std:error/Error
Error
error/RangeError
std:error/RangeError
RangeError
heap/ALIGN_LOG2
heap/ALIGN_SIZE
heap/ALIGN_MASK
heap/HEAP_OFFSET
heap/Heap
std:heap/ALIGN_LOG2
std:heap/ALIGN_SIZE
std:heap/ALIGN_MASK
std:heap/HEAP_OFFSET
std:heap/Heap
Heap
heap/Heap.used
heap/Heap.free
heap/Heap.size
heap/Heap.allocate
heap/Heap.dispose
heap/Heap.copy
map/Map
std:heap/Heap.used
std:heap/Heap.free
std:heap/Heap.size
std:heap/Heap.allocate
std:heap/Heap.dispose
std:heap/Heap.copy
std:heap/Heap.fill
std:heap/Heap.compare
std:map/Map
Map
set/Set
std:set/Set
Set
string/String
std:string/String
String
[program.exports]
array/Array
error/Error
error/RangeError
heap/Heap
map/Map
set/Set
string/String
std:array/Array
std:error/Error
std:error/RangeError
std:heap/Heap
std:map/Map
std:set/Set
std:string/String
;)

View File

@ -1,119 +0,0 @@
(module
(type $ii (func (param i32) (result i32)))
(type $iv (func (param i32)))
(type $v (func))
(global $heap/HEAP_OFFSET (mut i32) (i32.const 0))
(global $std/heap/ptr (mut i32) (i32.const 0))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "memory" (memory $0))
(start $start)
(func $heap/Heap.allocate (; 0 ;) (type $ii) (param $0 i32) (result i32)
(local $1 i32)
(local $2 i32)
(if
(i32.eqz
(get_local $0)
)
(return
(i32.const 0)
)
)
(set_local $1
(current_memory)
)
(if
(i32.gt_u
(i32.add
(get_global $heap/HEAP_OFFSET)
(get_local $0)
)
(i32.shl
(get_local $1)
(i32.const 16)
)
)
(if
(i32.lt_s
(grow_memory
(select
(tee_local $2
(i32.trunc_s/f64
(f64.ceil
(f64.div
(f64.convert_u/i32
(get_local $0)
)
(f64.const 65536)
)
)
)
)
(tee_local $1
(i32.sub
(i32.mul
(get_local $1)
(i32.const 2)
)
(get_local $1)
)
)
(i32.gt_s
(get_local $2)
(get_local $1)
)
)
)
(i32.const 0)
)
(unreachable)
)
)
(set_local $1
(get_global $heap/HEAP_OFFSET)
)
(if
(block (result i32)
(set_global $heap/HEAP_OFFSET
(i32.add
(get_global $heap/HEAP_OFFSET)
(get_local $0)
)
)
(i32.and
(get_global $heap/HEAP_OFFSET)
(i32.const 7)
)
)
(set_global $heap/HEAP_OFFSET
(i32.add
(i32.or
(get_global $heap/HEAP_OFFSET)
(i32.const 7)
)
(i32.const 1)
)
)
)
(get_local $1)
)
(func $start (; 1 ;) (type $v)
(local $0 i32)
(set_global $heap/HEAP_OFFSET
(get_global $HEAP_BASE)
)
(set_global $std/heap/ptr
(call $heap/Heap.allocate
(i32.const 10)
)
)
(block
(block $__inlined_func$heap/Heap.dispose
(set_local $0
(get_global $std/heap/ptr)
)
(nop)
)
)
)
)

File diff suppressed because it is too large Load Diff

View File

@ -1,2 +1,21 @@
let ptr: usize = Heap.allocate(10);
Heap.dispose(ptr);
const size: usize = 42;
let ptr1: usize = Heap.allocate(size);
let ptr2: usize = Heap.allocate(size);
assert(ptr1 != ptr2);
Heap.fill(ptr1, 0x12, size);
let i: usize;
for (i = 0; i < size; ++i)
assert(load<u8>(ptr1 + i) == 0x12);
Heap.copy(ptr2, ptr1, size);
for (i = 0; i < size; ++i)
assert(load<u8>(ptr2 + i) == 0x12);
assert(Heap.compare(ptr1, ptr2, size) == 0);
Heap.dispose(ptr1);
Heap.dispose(ptr2);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,52 @@
// a host-bindings syntax experiment
@binding(BindingCall.NEW, [ BindingType.STRING ], BindingType.OBJECT_HANDLE)
export class ExternalString {
@binding(BindingCall.FUNCTION, [ BindingType.U32, BindingType.U32 ], BindingType.OBJECT_HANDLE)
static fromCharCode(char: u16, schar: u16 = <u16>-1): String { return unreachable(); }
@binding(BindingCall.FUNCTION, [ BindingType.U32 ], BindingType.OBJECT_HANDLE)
static fromCodePoint(codepoint: u32): String { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.U32 ], BindingType.OBJECT_HANDLE)
charAt(index: u32): String { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.U32 ], BindingType.PASS_THRU)
charCodeAt(index: u32): u16 { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.U32 ], BindingType.PASS_THRU)
codePointAt(index: u32): u32 { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.OBJECT_HANDLE ], BindingType.OBJECT_HANDLE)
@operator("+")
concat(other: String): String { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.OBJECT_HANDLE ], BindingType.PASS_THRU)
endsWith(other: String): bool { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.OBJECT_HANDLE ], BindingType.PASS_THRU)
indexOf(other: String): i32 { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.OBJECT_HANDLE ], BindingType.PASS_THRU)
startsWith(other: String): bool { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.U32, BindingType.U32 ], BindingType.OBJECT_HANDLE)
substr(start: i32, length: i32): String { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.U32, BindingType.U32 ], BindingType.OBJECT_HANDLE)
substring(start: i32, end: i32): String { return unreachable(); }
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trim(): String { return unreachable(); }
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trimLeft(): String { return unreachable(); }
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trimRight(): String { return unreachable(); }
@binding(BindingCall.THIS, [ BindingType.OBJECT_HANDLE ], BindingType.PASS_THRU)
@operator("==")
equals(other: String): bool { return unreachable(); }
}

View File

@ -0,0 +1,65 @@
@binding(BindingCall.NEW, [BindingType.STRING], BindingType.OBJECT_HANDLE)
export class ExternalString {
@binding(BindingCall.FUNCTION, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE)
static fromCharCode(char: u16, schar: u16 = <u16>-1): String {
return unreachable();
}
@binding(BindingCall.FUNCTION, [BindingType.U32], BindingType.OBJECT_HANDLE)
static fromCodePoint(codepoint: u32): String {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.U32], BindingType.OBJECT_HANDLE)
charAt(index: u32): String {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.U32], BindingType.PASS_THRU)
charCodeAt(index: u32): u16 {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.U32], BindingType.PASS_THRU)
codePointAt(index: u32): u32 {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.OBJECT_HANDLE)
@operator("+")
concat(other: String): String {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU)
endsWith(other: String): bool {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU)
indexOf(other: String): i32 {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU)
startsWith(other: String): bool {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE)
substr(start: i32, length: i32): String {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.U32, BindingType.U32], BindingType.OBJECT_HANDLE)
substring(start: i32, end: i32): String {
return unreachable();
}
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trim(): String {
return unreachable();
}
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trimLeft(): String {
return unreachable();
}
@binding(BindingCall.THIS, [], BindingType.OBJECT_HANDLE)
trimRight(): String {
return unreachable();
}
@binding(BindingCall.THIS, [BindingType.OBJECT_HANDLE], BindingType.PASS_THRU)
@operator("==")
equals(other: String): bool {
return unreachable();
}
}