📄 asm.js
字号:
stream.write ("; -*- asm -*-\n");
/* Set the prev properties. */
var prev = null;
for (i = JSC$asm_head; i != null; prev = i, i = i.next)
i.prev = prev;
/*
* Fix the label line numbers to be the same that the next
* assembler operand has.
*/
last_ln = 0;
for (i = JSC$asm_tail; i != null; i = i.prev)
{
if (i.type == JSC$ASM_LABEL)
i.linenum = last_ln;
else if (typeof i.linenum != "undefined")
last_ln = i.linenum;
}
}
last_ln = 0;
for (i = JSC$asm_head; i != null; i = i.next)
{
if (typeof i.linenum == "undefined")
{
if (annotate)
stream.write ("; undefined linenum\n");
}
else
while (annotate && i.linenum > last_ln)
{
var line = src_stream.readln ();
stream.write ("; " + line + "\n");
last_ln++;
}
i.print (stream);
}
}
function JSC$asm_is_load_op (op)
{
return (op.type == JSC$OP_LOAD_GLOBAL
|| op.type == JSC$OP_LOAD_ARG
|| op.type == JSC$OP_LOAD_LOCAL);
}
function JSC$asm_is_store_op (op)
{
return (op.type == JSC$OP_STORE_GLOBAL
|| op.type == JSC$OP_STORE_ARG
|| op.type == JSC$OP_STORE_LOCAL);
}
function JSC$asm_is_local_jump (op)
{
return (op.type == JSC$OP_JMP
|| op.type == JSC$OP_IFFALSE
|| op.type == JSC$OP_IFTRUE
|| op.type == JSC$OP_IFFALSE_B
|| op.type == JSC$OP_IFTRUE_B
|| op.type == JSC$OP_TRY_PUSH);
}
function JSC$asm_is_const_op (op)
{
return (JSC$OP_CONST <= op.type && op.type <= JSC$OP_CONST_I3);
}
function JSC$asm_lookup_next_op (item)
{
while (item != null &&
(item.type == JSC$ASM_LABEL || item.type == JSC$ASM_SYMBOL))
item = item.next;
return item;
}
function JSC$asm_optimize (flags)
{
var item;
/* Simple peephole optimization. */
if ((flags & JSC$FLAG_OPTIMIZE_PEEPHOLE) != 0)
{
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/*
* Optimization for dup ... pop cases where pop removes the
* item duplicated by dup.
*/
if (item.next != null && item.next.type == JSC$OP_DUP)
{
var balance = 2;
var found = false;
var i1;
for (i1 = item.next.next;
i1 != null && i1.next != null;
i1 = i1.next)
{
var i2 = i1.next;
/*
* The lookup ends on branches, and on dup, throw,
* and try_pop operands. We optimize on a basic
* block and we match the closest dup-pop pairs.
*/
if (JSC$asm_is_local_jump (i1)
|| i1.type == JSC$OP_JSR
|| i1.type == JSC$OP_NEW
|| i1.type == JSC$OP_CALL_METHOD
|| i1.type == JSC$OP_RETURN
|| i1.type == JSC$ASM_SYMBOL
|| i1.type == JSC$ASM_LABEL
|| i1.type == JSC$OP_DUP
|| i1.type == JSC$OP_TRY_POP
|| i1.type == JSC$OP_THROW)
break;
if (i1.stack_delta)
{
balance += i1.stack_delta;
if (balance <= 0)
/* Going to negative. Stop here. */
break;
}
if (i2.type == JSC$OP_POP && balance == 1)
{
/* Found a matching pop. */
found = true;
i1.next = i2.next;
break;
}
}
if (found)
{
/* The dup can be removed. */
item.next = item.next.next;
}
}
/* Two instruction optimization (starting from item.next). */
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_APOP
&& i2.type == JSC$OP_POP)
{
/*
* i1: apop n
* i2: pop -> pop_n n + 1
*/
var i = new JSC$ASM_pop_n (i1.linenum, i1.value + 1);
item.next = i;
i.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_CONST_TRUE
&& (i2.type == JSC$OP_IFFALSE
|| i2.type == JSC$OP_IFFALSE_B))
{
/*
* i1: const_true
* i2: iffalse{,_b} .LX => ---
*/
item.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_CONST_FALSE
&& (i2.type == JSC$OP_IFTRUE
|| i2.type == JSC$OP_IFTRUE_B))
{
/*
* i1: const_false
* i2: iftrue{,_b} .LX => ---
*/
item.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if ((i1.type == JSC$OP_CONST_FALSE
&& (i2.type == JSC$OP_IFFALSE
|| i2.type == JSC$OP_IFFALSE_B))
|| (i1.type == JSC$OP_CONST_TRUE
&& (i2.type == JSC$OP_IFTRUE
|| i2.type == JSC$OP_IFTRUE_B)))
{
/*
* i1: const_false
* i2: iffalse{,_b} .LX => jmp .LX
*
* i1: const_true
* i2: iftrue{,_b} .LX => jmp .LX
*/
var i = new JSC$ASM_jmp (i1.linenum, i2.value);
item.next = i;
i.next = i2.next;
}
}
}
}
/* Jumps to jumps. */
if ((flags & JSC$FLAG_OPTIMIZE_JUMPS) != 0)
{
if (JSC$verbose)
JSC$message ("jsc: optimize: jumps to jumps");
for (item = JSC$asm_head; item != null; item = item.next)
if (JSC$asm_is_local_jump (item))
{
var i2;
/* Operand's value is a label */
i2 = JSC$asm_lookup_next_op (item.value);
if (i2 != null && i2.type == JSC$OP_JMP)
/* Ok, we can jump there directly. */
item.value = i2.value;
}
}
if ((flags & JSC$FLAG_OPTIMIZE_HEAVY) != 0)
JSC$optimize_heavy ();
/*
* Optimizations for the size of the generated byte-code. It isn't
* probably worth of doing these optimization for interactive
* scripts since these won't affect the speed of the execution.
* However, these optimizations make the byte-code files smaller so
* these are nice for batch-compiled files.
*/
if ((flags & JSC$FLAG_OPTIMIZE_BC_SIZE) != 0)
{
var delta = true;
while (delta)
{
delta = false;
/* Remove un-referenced labels. */
if (JSC$verbose)
JSC$message ("jsc: optimize: removing un-referenced labels");
/* First, make all labels unreferenced. */
for (item = JSC$asm_head; item != null; item = item.next)
if (item.type == JSC$ASM_LABEL)
item.referenced = false;
/* Second, mark all referenced labels. */
for (item = JSC$asm_head; item != null; item = item.next)
if (JSC$asm_is_local_jump (item))
item.value.referenced = true;
/* Third, remove all un-referenced labels. */
for (item = JSC$asm_head; item != null; item = item.next)
while (item.next != null && item.next.type == JSC$ASM_LABEL
&& !item.next.referenced
&& item.next.next != null)
{
delta = true;
item.next = item.next.next;
}
/* Dead code elimination. */
if (JSC$verbose)
JSC$message ("jsc: optimize: dead code elimination");
for (item = JSC$asm_head; item != null; item = item.next)
if (item.type == JSC$OP_RETURN || item.type == JSC$OP_JMP)
while (item.next != null && item.next.type != JSC$ASM_SYMBOL
&& item.next.type != JSC$ASM_LABEL)
{
delta = true;
item.next = item.next.next;
}
/* Simple peephole optimization. */
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/* Two instruction optimization (starting from item.next). */
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_JMP
&& i2.type == JSC$ASM_LABEL
&& i1.value == i2)
{
/*
* i1: jmp .LX
* i2: .LX => .LX
*/
item.next = i2;
delta = true;
}
}
}
}
}
}
function JSC$optimize_heavy ()
{
if (JSC$verbose)
JSC$message ("jsc: optimize: liveness analyzing");
/* First, set the prev pointers and zero usage flags. */
var item, prev = null;
for (item = JSC$asm_head; item != null; prev = item, item = item.next)
{
item.prev = prev;
item.live_args = 0;
item.live_locals = 0;
item.live_used = false;
}
/* For each function. */
var ftail, fhead;
for (ftail = JSC$asm_tail; ftail != null; ftail = fhead.prev)
{
var change = true;
/* While there is change in the liveness. */
while (change)
{
change = false;
for (fhead = ftail;
fhead.type != JSC$ASM_SYMBOL;
fhead = fhead.prev)
{
var floc, farg;
if (fhead.next != null)
{
floc = fhead.next.live_locals;
farg = fhead.next.live_args;
}
else
floc = farg = 0;
if (fhead.type == JSC$OP_LOAD_LOCAL && fhead.value < 32)
floc |= (1 << fhead.value);
if (fhead.type == JSC$OP_STORE_LOCAL && fhead.value < 32)
floc &= ~(1 << fhead.value);
if (fhead.type == JSC$OP_LOAD_ARG && fhead.value < 32)
farg |= (1 << fhead.value);
if (fhead.type == JSC$OP_STORE_ARG && fhead.value < 32)
farg &= ~(1 << fhead.value);
if (JSC$asm_is_local_jump (fhead))
{
floc |= fhead.value.live_locals;
fhead.value.live_used = true;
}
if (fhead.live_used && (fhead.live_locals != floc
|| fhead.live_args != farg))
change = true;
fhead.live_used = false;
fhead.live_locals = floc;
fhead.live_args = farg;
}
}
}
/*
* When we have the liveness analyzing performed, we can do some
* fancy optimizations.
*/
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/* Three instruction optimization. */
if (item.next != null && item.next.next != null
&& item.next.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
var i3 = i2.next;
if (i1.type == JSC$OP_STORE_LOCAL
&& i2.type == JSC$OP_LOAD_LOCAL
&& i1.value == i2.value
&& (i3.live_locals & (1 << i1.value)) == 0)
{
/*
* i1: store_local n
* i2: load_local n
* i3: nnn (n not live) => nnn
*/
item.next = i3;
}
}
}
}
function JSC$asm_finalize ()
{
var item;
var offset = 0;
for (item = JSC$asm_head; item != null; item = item.next)
{
item.offset = offset;
offset += item.size;
}
}
function JSC$ConstantReg ()
{
}
function JSC$asm_genconstant (val)
{
if (JSC$asm_known_constants == null)
JSC$asm_known_constants = new JSC$ConstantReg ();
/* Lookup <val> from a list of known constants. */
var id = JSC$asm_known_constants[val];
if (typeof id == "number")
return id;
/* This is a new constant. */
JSC$asm_constants.append (val);
JSC$asm_known_constants[val] = JSC$asm_constcount;
return JSC$asm_constcount++;
}
function JSC$asm_bytecode ()
{
var item;
var symtab = new String ("");
var nsymtab_entries = 0;
var code = new String ("");
var debug = new String ("");
var debug_last_linenum = 0;
if (JSC$verbose)
JSC$message ("jsc: generating byte-code");
if (JSC$generate_debug_info)
/* Source file name. */
debug.append (String.pack ("CN", JSC$DEBUG_FILENAME, JSC$filename.length)
+ JSC$filename);
JSC$asm_constants = new String ("");
for (item = JSC$asm_head; item != null; item = item.next)
{
if (item.type == JSC$ASM_SYMBOL)
{
symtab.append (item.value + String.pack ("CN", 0, item.offset));
nsymtab_entries++;
}
else if (item.type == JSC$ASM_LABEL)
;
else
{
/* Real assembler operands. */
if (JSC$generate_debug_info)
if (item.linenum != debug_last_linenum)
{
debug.append (String.pack ("CNN", JSC$DEBUG_LINENUMBER,
item.offset + item.size,
item.linenum));
debug_last_linenum = item.linenum;
}
if (item.size == 1)
/* We handle these. */
code.append (String.pack ("C", item.type));
else
{
/*
* All operands which take an argument, have a method to create
* the byte code entry for their argument.
*/
code.append (String.pack ("C", item.type) + item.bytecode ());
}
}
}
symtab = String.pack ("N", nsymtab_entries) + symtab;
if (JSC$verbose)
{
var msg = ("jsc: code=" + code.length.toString ()
+ ", constants=" + JSC$asm_constants.length.toString ()
+ ", symtab=" + symtab.length.toString ());
if (JSC$generate_debug_info)
msg += ", debug=" + debug.length.toString ();
msg += (", headers="
+ (32 + (JSC$generate_debug_info ? 8 : 0)).toString ()
+ ", total="
+ (code.length + JSC$asm_constants.length + symtab.length
+ debug.length + 32
+ (JSC$generate_debug_info ? 8 : 0)).toString ());
JSC$message (msg);
}
return (String.pack ("NN", JSC$BC_MAGIC,
3 + (JSC$generate_debug_info ? 1 : 0))
+ String.pack ("NN", JSC$BC_SECT_CODE, code.length) + code
+ String.pack ("NN", JSC$BC_SECT_CONSTANTS,
JSC$asm_constants.length)
+ JSC$asm_constants
+ String.pack ("NN", JSC$BC_SECT_SYMTAB, symtab.length) + symtab
+ (JSC$generate_debug_info
? String.pack ("NN", JSC$BC_SECT_DEBUG, debug.length) + debug
: ""));
}
/*
Local variables:
mode: c
End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -