Skip to content

Commit c44d020

Browse files
committed
Merge remote-tracking branch 'origin/master' into release
2 parents 4053274 + 289ddb7 commit c44d020

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+6271
-6496
lines changed

lib/rtrace/index.d.ts

Lines changed: 48 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,63 @@
1-
/**
2-
* This method returns an `RTrace` object used for reference count tracing.
3-
*
4-
* @param {TraceEventCallback?} onerror - A method that is called when a trace error event occurs.
5-
* @param {TraceEventCallback?} oninfo - A method that is called when a trace info event occurs.
6-
*/
7-
declare function rtrace(onerror?: rtrace.TraceEventCallback, oninfo?: rtrace.TraceEventCallback): rtrace.RTrace;
1+
/** Creates a new `RTrace` instance, tracking allocations, frees and reference counts. */
2+
declare function rtrace(
3+
/** Function being called when a problem is detected. */
4+
onerror?: (error: Error) => void,
5+
/** Function being called with information messages. */
6+
oninfo?: (info: string) => void
7+
): rtrace.RTrace;
88

99
declare namespace rtrace {
10-
/**
11-
* The RTrace interface represents a collection of properties that help develoeprs describe the
12-
* reference counts and state of the Web Assembly module.
13-
*/
10+
/** The rtrace instance used as the `rtrace` import to the Wasm module. */
1411
export interface RTrace {
15-
16-
/**
17-
* The current allocation count.
18-
*/
12+
/** Number of allocations so far. */
1913
allocCount: number;
20-
21-
/**
22-
* The current free count.
23-
*/
14+
/** Number of reallocations so far. */
15+
reallocCount: number;
16+
/** Number of frees so far. */
2417
freeCount: number;
25-
26-
/**
27-
* The current increment count.
28-
*/
18+
/** Number of RC increments (retains) so far. */
2919
incrementCount: number;
30-
31-
/**
32-
* The current decrement count.
33-
*/
20+
/** Number of RC decrements (releases) so far. */
3421
decrementCount: number;
3522

36-
/**
37-
* This method is called when an allocation occurs.
38-
*
39-
* @param {number} block - The `ptr - 16` value indicating the start of the block.
40-
*/
41-
onalloc(block: number): void;
42-
43-
/**
44-
* This method is called when a block is freed.
45-
*
46-
* @param {number} block - The `ptr - 16` value indicating the start of the block.
47-
*/
48-
onfree(block: number): void;
49-
50-
/**
51-
* This method is called when a reference count is incrememnted
52-
*
53-
* @param {number} block - The `ptr - 16` value indicating the start of the block.
54-
*/
55-
onincrement(block: number): void;
56-
57-
/**
58-
* This method is called when a reference count is decremented.
59-
*
60-
* @param {number} block - The `ptr - 16` value indicating the start of the block.
61-
*/
62-
ondecrement(block: number): void;
63-
64-
/**
65-
* This property indicates if rtrace is active.
66-
*/
23+
/** Called when a new block is allocated. */
24+
onalloc(
25+
/** New block being allocated. */
26+
block: number
27+
): void;
28+
29+
/** Called when a block is reallocated and must be moved. */
30+
onrealloc(
31+
/** Block being moved. */
32+
oldBlock: number,
33+
/** New block used from now on. */
34+
newBlock: number
35+
): void;
36+
37+
/** Called when a block is freed, implicitly or explicitly. */
38+
onfree(
39+
/** Block being freed. */
40+
block: number
41+
): void;
42+
43+
/** Called when a reference to a block is retained (RC incremented by one). */
44+
onincrement(
45+
/** Block a reference to is being retained. */
46+
block: number
47+
): void;
48+
49+
/** Called when a reference to a block is released (RC decremented by one). */
50+
ondecrement(
51+
/** Block a reference to is being released. */
52+
block: number
53+
): void;
54+
55+
/** Checks if rtrace is active, i.e. at least one event has occurred. */
6756
readonly active: boolean;
6857

69-
/**
70-
* This method returns the current number of allocated blocks.
71-
*/
58+
/** Checks if there are any leaks and emits them via `oninfo`. Returns the number of live blocks. */
7259
check(): number;
73-
7460
}
75-
76-
/**
77-
* This is a trace event callback. It accepts a string containing a message about the event.
78-
*/
79-
export type TraceEventCallback = (info: string) => void;
8061
}
8162

8263
export = rtrace;

lib/rtrace/index.js

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ function rtrace(onerror, oninfo) {
55
var rtrace = {
66

77
allocCount: 0,
8+
reallocCount: 0,
89
freeCount: 0,
910
incrementCount: 0,
1011
decrementCount: 0,
@@ -19,6 +20,26 @@ function rtrace(onerror, oninfo) {
1920
}
2021
},
2122

23+
onrealloc: function(oldBlock, newBlock) {
24+
++rtrace.reallocCount;
25+
if (!blocks.has(oldBlock)) {
26+
onerror(Error("orphaned realloc (old): " + oldBlock));
27+
} else {
28+
if (!blocks.has(newBlock)) {
29+
onerror(Error("orphaned realloc (new): " + newBlock));
30+
} else {
31+
let newRc = blocks.get(newBlock);
32+
if (newRc != 0) {
33+
onerror(Error("invalid realloc: " + oldBlock + " -> " + newBlock + " @ " + newRc));
34+
} else {
35+
let oldRc = blocks.get(oldBlock);
36+
oninfo("REALLOC " + oldBlock + " @ " + oldRc + " -> " + newBlock);
37+
blocks.set(newBlock, oldRc);
38+
}
39+
}
40+
}
41+
},
42+
2243
onfree: function(block) {
2344
++rtrace.freeCount;
2445
if (!blocks.has(block)) {
@@ -47,7 +68,7 @@ function rtrace(onerror, oninfo) {
4768
} else {
4869
let rc = blocks.get(block);
4970
if (rc < 1) {
50-
onerror(Error("invalid decrement: " + block));
71+
onerror(Error("invalid decrement: " + block + " @ " + rc));
5172
} else {
5273
oninfo("-- " + block + " @ " + rc + "->" + (rc - 1));
5374
blocks.set(block, rc - 1);
@@ -56,7 +77,7 @@ function rtrace(onerror, oninfo) {
5677
},
5778

5879
get active() {
59-
return Boolean(rtrace.allocCount || rtrace.freeCount || rtrace.incrementCount || rtrace.decrementCount);
80+
return Boolean(rtrace.allocCount || rtrace.reallocCount || rtrace.freeCount || rtrace.incrementCount || rtrace.decrementCount);
6081
},
6182

6283
check() {

std/assembly/array.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ function ensureSize(array: usize, minSize: usize, alignLog2: u32): void {
1414
if (minSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
1515
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).buffer);
1616
let newCapacity = minSize << alignLog2;
17-
let newData = __realloc(oldData, newCapacity);
17+
let newData = __realloc(oldData, newCapacity); // keeps RC
1818
memory.fill(newData + oldCapacity, 0, newCapacity - oldCapacity);
1919
if (newData !== oldData) { // oldData has been free'd
20-
store<usize>(array, __retain(newData), offsetof<ArrayBufferView>("buffer"));
20+
store<usize>(array, newData, offsetof<ArrayBufferView>("buffer"));
2121
store<usize>(array, newData, offsetof<ArrayBufferView>("dataStart"));
2222
}
2323
store<u32>(array, newCapacity, offsetof<ArrayBufferView>("byteLength"));

std/assembly/rt/rtrace.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BLOCK } from "./common";
22

33
export declare function onalloc(s: BLOCK): void;
4+
export declare function onrealloc(sOld: BLOCK, sNew: BLOCK): void;
45
export declare function onincrement(s: BLOCK): void;
56
export declare function ondecrement(s: BLOCK): void;
67
export declare function onfree(s: BLOCK): void;

std/assembly/rt/tlsf.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { AL_BITS, AL_MASK, DEBUG, BLOCK, BLOCK_OVERHEAD, BLOCK_MAXSIZE } from "rt/common";
2-
import { onfree, onalloc } from "./rtrace";
2+
import { onfree, onalloc, onrealloc } from "./rtrace";
33
import { REFCOUNT_MASK } from "./pure";
44

55
/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator ///////////////////////
@@ -486,7 +486,7 @@ export function maybeInitialize(): Root {
486486
var collectLock: bool = false;
487487

488488
/** Allocates a block of the specified size. */
489-
export function allocateBlock(root: Root, size: usize): Block {
489+
export function allocateBlock(root: Root, size: usize, id: u32): Block {
490490
if (DEBUG) assert(!collectLock); // must not allocate while collecting
491491
var payloadSize = prepareSize(size);
492492
var block = searchBlock(root, payloadSize);
@@ -509,7 +509,7 @@ export function allocateBlock(root: Root, size: usize): Block {
509509
}
510510
if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= payloadSize); // must fit
511511
block.gcInfo = 0; // RC=0
512-
// block.rtId = 0; // set by the caller (__alloc)
512+
block.rtId = id;
513513
block.rtSize = size;
514514
removeBlock(root, <Block>block);
515515
prepareBlock(root, <Block>block, payloadSize);
@@ -521,12 +521,6 @@ export function allocateBlock(root: Root, size: usize): Block {
521521
export function reallocateBlock(root: Root, block: Block, size: usize): Block {
522522
var payloadSize = prepareSize(size);
523523
var blockInfo = block.mmInfo;
524-
if (DEBUG) {
525-
assert(
526-
!(blockInfo & FREE) && // must be used
527-
!(block.gcInfo & ~REFCOUNT_MASK) // not buffered or != BLACK
528-
);
529-
}
530524

531525
// possibly split and update runtime size if it still fits
532526
if (payloadSize <= (blockInfo & ~TAGS_MASK)) {
@@ -552,44 +546,53 @@ export function reallocateBlock(root: Root, block: Block, size: usize): Block {
552546
}
553547

554548
// otherwise move the block
555-
var newBlock = allocateBlock(root, size);
556-
newBlock.rtId = block.rtId;
549+
var newBlock = allocateBlock(root, size, block.rtId); // may invalidate cached blockInfo
550+
newBlock.gcInfo = block.gcInfo; // keep RC
557551
memory.copy(changetype<usize>(newBlock) + BLOCK_OVERHEAD, changetype<usize>(block) + BLOCK_OVERHEAD, size);
558552
if (changetype<usize>(block) >= __heap_base) {
559-
block.mmInfo = blockInfo | FREE;
560-
insertBlock(root, block);
561-
if (isDefined(ASC_RTRACE)) onfree(block);
553+
if (isDefined(ASC_RTRACE)) onrealloc(block, newBlock);
554+
freeBlock(root, block);
562555
}
563556
return newBlock;
564557
}
565558

566559
/** Frees a block. */
567560
export function freeBlock(root: Root, block: Block): void {
568561
var blockInfo = block.mmInfo;
569-
assert(!(blockInfo & FREE)); // must be used (user might call through to this)
570562
block.mmInfo = blockInfo | FREE;
571563
insertBlock(root, block);
572564
if (isDefined(ASC_RTRACE)) onfree(block);
573565
}
574566

567+
/** Checks that a used block is valid to be freed or reallocated. */
568+
function checkUsedBlock(ref: usize): Block {
569+
var block = changetype<Block>(ref - BLOCK_OVERHEAD);
570+
assert(
571+
ref != 0 && !(ref & AL_MASK) && // must exist and be aligned
572+
!(block.mmInfo & FREE) && // must be used
573+
!(block.gcInfo & ~REFCOUNT_MASK) // not buffered or != BLACK
574+
);
575+
return block;
576+
}
577+
575578
// @ts-ignore: decorator
576579
@global @unsafe
577580
export function __alloc(size: usize, id: u32): usize {
578-
var block = allocateBlock(maybeInitialize(), size);
579-
block.rtId = id;
580-
return changetype<usize>(block) + BLOCK_OVERHEAD;
581+
return changetype<usize>(
582+
allocateBlock(maybeInitialize(), size, id)
583+
) + BLOCK_OVERHEAD;
581584
}
582585

583586
// @ts-ignore: decorator
584587
@global @unsafe
585588
export function __realloc(ref: usize, size: usize): usize {
586-
assert(ref != 0 && !(ref & AL_MASK)); // must exist and be aligned
587-
return changetype<usize>(reallocateBlock(maybeInitialize(), changetype<Block>(ref - BLOCK_OVERHEAD), size)) + BLOCK_OVERHEAD;
589+
return changetype<usize>(
590+
reallocateBlock(maybeInitialize(), checkUsedBlock(ref), size)
591+
) + BLOCK_OVERHEAD;
588592
}
589593

590594
// @ts-ignore: decorator
591595
@global @unsafe
592596
export function __free(ref: usize): void {
593-
assert(ref != 0 && !(ref & AL_MASK)); // must exist and be aligned
594-
freeBlock(maybeInitialize(), changetype<Block>(ref - BLOCK_OVERHEAD));
597+
freeBlock(maybeInitialize(), checkUsedBlock(ref));
595598
}

tests/allocators/rt-full/optimized.wat

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
(type $i32_i32_=>_none (func (param i32 i32)))
44
(type $i32_i32_i32_=>_none (func (param i32 i32 i32)))
55
(type $i32_=>_i32 (func (param i32) (result i32)))
6-
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
76
(type $none_=>_none (func))
7+
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
88
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
99
(type $none_=>_i32 (func (result i32)))
10+
(type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32)))
1011
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
1112
(memory $0 1)
1213
(data (i32.const 16) "(\00\00\00\01\00\00\00\01\00\00\00(\00\00\00a\00l\00l\00o\00c\00a\00t\00i\00o\00n\00 \00t\00o\00o\00 \00l\00a\00r\00g\00e")
@@ -976,15 +977,15 @@
976977
i32.store
977978
end
978979
)
979-
(func $~lib/rt/tlsf/allocateBlock (; 15 ;) (param $0 i32) (param $1 i32) (result i32)
980-
(local $2 i32)
980+
(func $~lib/rt/tlsf/allocateBlock (; 15 ;) (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
981981
(local $3 i32)
982+
(local $4 i32)
982983
local.get $0
983984
local.get $1
984985
call $~lib/rt/tlsf/prepareSize
985-
local.tee $3
986+
local.tee $4
986987
call $~lib/rt/tlsf/searchBlock
987-
local.tee $2
988+
local.tee $3
988989
i32.eqz
989990
if
990991
i32.const 1
@@ -993,43 +994,43 @@
993994
i32.const 0
994995
global.set $~lib/rt/tlsf/collectLock
995996
local.get $0
996-
local.get $3
997+
local.get $4
997998
call $~lib/rt/tlsf/searchBlock
998-
local.tee $2
999+
local.tee $3
9991000
i32.eqz
10001001
if
10011002
local.get $0
1002-
local.get $3
1003+
local.get $4
10031004
call $~lib/rt/tlsf/growMemory
10041005
local.get $0
1005-
local.get $3
1006+
local.get $4
10061007
call $~lib/rt/tlsf/searchBlock
1007-
local.set $2
1008+
local.set $3
10081009
end
10091010
end
1010-
local.get $2
1011+
local.get $3
10111012
i32.const 0
10121013
i32.store offset=4
1014+
local.get $3
10131015
local.get $2
1016+
i32.store offset=8
1017+
local.get $3
10141018
local.get $1
10151019
i32.store offset=12
10161020
local.get $0
1017-
local.get $2
1021+
local.get $3
10181022
call $~lib/rt/tlsf/removeBlock
10191023
local.get $0
1020-
local.get $2
10211024
local.get $3
1025+
local.get $4
10221026
call $~lib/rt/tlsf/prepareBlock
1023-
local.get $2
1027+
local.get $3
10241028
)
10251029
(func $~lib/rt/tlsf/__alloc (; 16 ;) (param $0 i32) (param $1 i32) (result i32)
10261030
call $~lib/rt/tlsf/maybeInitialize
10271031
local.get $0
1028-
call $~lib/rt/tlsf/allocateBlock
1029-
local.tee $0
10301032
local.get $1
1031-
i32.store offset=8
1032-
local.get $0
1033+
call $~lib/rt/tlsf/allocateBlock
10331034
i32.const 16
10341035
i32.add
10351036
)

0 commit comments

Comments
 (0)