AS3, optional type annotation, and strict mode

Boy, this is my first English post on this blog. I wish I had the time to write more English and Japanses posts. Anyway here we go.

Those who have been using ActionScript 3 would have noticed its optional type annotations. So what do we get from being explicit about types? It's obvious that, by stating the programs' invariants through type annotation, type errors can be reported earlier (at compile time) than they would have been otherwise (at runtime). But what else? Does explicit typing yield better runtime performance too?

On the current implementation of Adobe's ActionScript 3 compiler, the answer to that may just be a _no_. Even though Flex SDK and Tamarin are both open source, which means that we can get full sources to the whole compiler+vm suite to get AS3 running, it's still hard to figure things out just by browsing through the code without detailed documentations. So I'm just gonna do some superficial tests to see what's going on in the AS3 compiler.

=======================================================================================================

When compiling AS3 source code, there are two compilation modes: the standard mode and the strict mode. In standard mode, everything's supposed to be compatible to the good old ECMA-262 version 3 spec, so we should be able to safely assume that the type annotations in standard mode doesn't do much. In strict mode, however, some type checks can be done at compile time, so that: we can't assign to a variable not yet declared, neither can we call a function with parameters of imcompatible types.

According to the ES4 wiki, [url=http://wiki.ecmascript.org/doku.php?id=proposals:strict_and_standard_modes]Strict and Standard compilation modes[/url], strict mode is [i]"only meant as a way for programmers to indicate their desire for more extensive and conservative error analysis"[/i], and does not imply optimizations, although implementation can take advantage of the type information to produce better code.

So let's take a look at this sample code snippet, to see how type annotations affect type checking and instruction selection in Adobe's AS3 compiler:
test.as:
package {

class TestClass {
function foo(x : int, y : int) : int {
return x + y;
}

function goo(x, y) {
return foo(x, y);
}

function hoo(x, y : String) {
return x + y;
}

function ioo(x : int, y : int) {
var i = x + y;
return i;
}

function joo(x : int, y : int) {
var i : int = x + y;
return i;
}
}

var c = new TestClass();
var i = c.goo(1, "2");
print(i); // 3
i = c.hoo(1, "2");
print(i); // 12
i = c.ioo(1, "2");
print(i); // 3
}

(compiled with asc.jar from Flex 3 SDK, with the command:
java -jar asc.jar -import builtin.abc -import toplevel.abc -m -strict test.as
builtin.abc and toplevel.abc are from Tamarin, 2007/10/31)

The methods in the code snippet are pretty much the same: they *add up* the two parameters. Foo() is fully type annotated, goo() is totally unannotated, where as the other methods are partially annotated.
Note that there are two AVM2 instructions that does the operation of "add": one is the general "add", the other is "add_i", which adds up two integers.
As stated in [url=http://www.adobe.com/devnet/actionscript/articles/avm2overview.pdf]ActionScript Virtual Machine 2 Overview[/url], the semantics of add and add_i are:
[quote="ActionScript Virtual Machine 2 (AVM2) Overview"][Append][Append][Append] method.
4. If none of the above apply, convert value1 and value2 to primitives. This is done by calling ToPrimitive with no hint. This results in value1_primitive and value2_primitive. If value1_primitive or value2_primitive is a String then convert both to Strings using the ToString algorithm (ECMA-262 section 9.8), concatenate the results, and set value3 to the concatenated String. Otherwise convert both to Numbers using the ToNumber algorithm (ECMA-262 section 9.3), add the results, and set value3 to the result of the addition.
Push value3 onto the stack.
Notes
For more information, see ECMA-262 section 11.6 (“Additive Operators”) and ECMA-357 section 11.4.[/quote]
[quote="ActionScript Virtual Machine 2 (AVM2) Overview"]add_i
Operation
Add two integer values.
Format
add_i
Forms
add_i = 197 (0xc5)
Stack
…, value1, value2 => …, value3
Description
Pop value1 and value2 off of the stack and convert them to int values using the ToInt32 algorithm (ECMA-262 section 9.5). Add the two int values and push the result onto the stack.[/quote]
Both instructions aren't really low-level enough. Whatever parameters passed in, both instructions include the semantics to do a type check/implicit type convertion first, then add the two operands, and finally pushing the result back onto the evaluation stack. Anyway, if it is known that both the operands are ints, I reckon it should be better to choose add_i than add. But that's not the case here, Adobe's AS3 compiler always generated code using add.

Take a closer look at the code generated for foo().
The method's generated signature is:
[code] 1. MethodInfo param_count=2 return_type=1 param_types={ 1 1 } debug_name_index=2 needs_arguments=false need_rest=false needs_activation=false has_optional=false ignore_rest=false native=false has_param_names =false -> 1[/code]
The return type and parameter types are all int, correct.
The method's body:
[code]MethodBody max_stack=2 max_locals=3 scope_depth=4 max_scope=5 code_length=6 traits_count=0 -> 1[/code]
[code]// ++StartMethod foo$0

LoadThis
0:Getlocal0 [1]

PushScope
1:Pushscope [0]

LoadRegister 1, int
2:Getlocal1 [1]

LoadRegister 2, int
3:Getlocal2 [2]

InvokeBinary BinaryPlusOp
4:Add [1]

Return
5:Returnvalue [0]

// --FinishMethod foo$0 TestClass/foo[/code]
So even when fully type annotated as in foo(), the compiler chooses add instead of add_i. Actually the compiler seem to generate the same code for foo()'s method body whatever the compilation mode.

For goo(), we can see that calling goo() with (1, "2") yields a number 3. The string "2" is implicitly converted into int upon calling foo() in goo(), even if that wasn't the programmer's intention.
The compiler doesn't reject methods calls where formal parameters' types and actual parameters' types aren't exactly the same. The types are considered "compatible" if there's a way to implicitly convert from the source type to the target type. So unless we do something like this:
class A { }
class B { }
function test(a : A) {
print("A");
}
test(new B()); // compile time error
// [Compiler] Error #1067: Implicit coercion of a value of type B to an unrelated type A.

the compiler's gonna be "forgiving" enough not to warn you that implicit conversions have been done. Not to mention, code like this will also compile, whatever the compilation mode:
c.ioo(1);

No errors, no warnings. Not until you run the code will you see the argument error:
[quote]ArgumentError: Error #1063[/quote]

I thought strict mode was supposed to be less forgiving than this...well, I'm wrong.

It's easy to see that the compiler doesn't do type inference at all. In ioo(), where I didn't annotate the local variable's type, the compiler adds a coerce instruction after the add:
[code]// ++StartMethod ioo$0

LoadThis
0:Getlocal0 [1]

PushScope
1:Pushscope [0]

LoadRegister 1, int
2:Getlocal1 [1]

LoadRegister 2, int
3:Getlocal2 [2]

InvokeBinary BinaryPlusOp
4:Add [1]

CheckType *
5:Coerce.o [1] // coerce here

StoreRegister 3, *
6:Setlocal3 [0]

LoadRegister 3, *
7:Getlocal3 [1]

Return
8:Returnvalue [0]

// --FinishMethod ioo$0 TestClass/ioo[/code]
Again, the code generated for ioo()'s method body is the same whatever the compilation mode. Overall, there's only little difference between code generated in strict mode or standard mode.

Haven't run any benchmarks for differently annotated code, I'd just guess from the code generated that strict mode with annotated types in AS3 doesn't imply better runtime performance. I'm asking [url=http://blogs.adobe.com/fcheng/]Francis Cheng[/url] on this, and see if he can give us more detail on optional type annotations and strict mode.

I thought we can take advantage of the declared types and at least reduce some of these redundant type checks/coerces. Adobe's AS3 compiler doesn't do that currently. And there should be a reason behind it, which I don't know. Maybe digging into the source code will give me a better picture...

=======================================================================================================

The only reason I can come up with is, the optimization on reducing dynamic type checking is not done at compile time, and is instead done at runtime, through a tracing-JIT. [url=http://www.bluishcoder.co.nz/2008/02/quick-introduction-to-tamarin-tracing.html]Tamarin-tracing[/url] is the one that
[quote]traces code executing during hotspots and compiles it so when those hotspots are entered again the compiled code is run instead. It traces each statement executed, including within other function calls, and this entire execution path is compiled.[/quote]
Maybe the JITter knows how to reduce all those redundant type checks on frequently used types, so no matter what compilation mode you're using, you always benefit from the runtime.

=======================================================================================================

The Impact of Optional Type Information on JIt Compilation of Dynammically Typed Languages
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值